IteratorRewriter.vb 21.6 KB
Newer Older
1
' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3

Imports System.Collections.Immutable
4
Imports System.Runtime.InteropServices
5
Imports Microsoft.CodeAnalysis.CodeGen
P
Pilchie 已提交
6 7 8 9 10
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols

Namespace Microsoft.CodeAnalysis.VisualBasic

    Partial Friend NotInheritable Class IteratorRewriter
11
        Inherits StateMachineRewriter(Of FieldSymbol)
P
Pilchie 已提交
12

13 14
        Private ReadOnly _elementType As TypeSymbol
        Private ReadOnly _isEnumerable As Boolean
P
Pilchie 已提交
15

16 17
        Private _currentField As FieldSymbol
        Private _initialThreadIdField As FieldSymbol
P
Pilchie 已提交
18 19

        Public Sub New(body As BoundStatement,
20 21
                       method As MethodSymbol,
                       isEnumerable As Boolean,
22
                       stateMachineType As IteratorStateMachine,
23 24 25
                       slotAllocatorOpt As VariableSlotAllocator,
                       compilationState As TypeCompilationState,
                       diagnostics As DiagnosticBag)
P
Pilchie 已提交
26

27
            MyBase.New(body, method, stateMachineType, slotAllocatorOpt, compilationState, diagnostics)
P
Pilchie 已提交
28

29
            Me._isEnumerable = isEnumerable
P
Pilchie 已提交
30 31 32

            Dim methodReturnType = method.ReturnType
            If methodReturnType.GetArity = 0 Then
33
                Me._elementType = method.ContainingAssembly.GetSpecialType(SpecialType.System_Object)
P
Pilchie 已提交
34 35
            Else
                ' the element type may contain method type parameters, which are now alpha-renamed into type parameters of the generated class
36
                Me._elementType = DirectCast(methodReturnType, NamedTypeSymbol).TypeArgumentsNoUseSiteDiagnostics().Single().InternalSubstituteTypeParameters(Me.TypeMap)
P
Pilchie 已提交
37 38 39 40 41 42 43 44
            End If
        End Sub

        ''' <summary>
        ''' Rewrite an iterator method into a state machine class.
        ''' </summary>
        Friend Overloads Shared Function Rewrite(body As BoundBlock,
                                                 method As MethodSymbol,
T
TomasMatousek 已提交
45
                                                 methodOrdinal As Integer,
46
                                                 slotAllocatorOpt As VariableSlotAllocator,
P
Pilchie 已提交
47
                                                 compilationState As TypeCompilationState,
48 49
                                                 diagnostics As DiagnosticBag,
                                                 <Out> ByRef stateMachineType As IteratorStateMachine) As BoundBlock
P
Pilchie 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

            If body.HasErrors Or Not method.IsIterator Then
                Return body
            End If

            Dim methodReturnType As TypeSymbol = method.ReturnType

            Dim retSpecialType = method.ReturnType.OriginalDefinition.SpecialType
            Dim isEnumerable As Boolean = retSpecialType = SpecialType.System_Collections_Generic_IEnumerable_T OrElse
                                          retSpecialType = SpecialType.System_Collections_IEnumerable

            Dim elementType As TypeSymbol
            If method.ReturnType.IsDefinition Then
                elementType = method.ContainingAssembly.GetSpecialType(SpecialType.System_Object)
            Else
                elementType = DirectCast(methodReturnType, NamedTypeSymbol).TypeArgumentsNoUseSiteDiagnostics(0)
            End If

T
TomasMatousek 已提交
68
            stateMachineType = New IteratorStateMachine(slotAllocatorOpt, compilationState, method, methodOrdinal, elementType, isEnumerable)
69

70
            compilationState.ModuleBuilderOpt.CompilationState.SetStateMachineType(method, stateMachineType)
P
Pilchie 已提交
71

72
            Dim rewriter As New IteratorRewriter(body, method, isEnumerable, stateMachineType, slotAllocatorOpt, compilationState, diagnostics)
P
Pilchie 已提交
73 74 75 76 77 78 79 80 81 82 83 84

            ' check if we have all the types we need
            If rewriter.EnsureAllSymbolsAndSignature() Then
                Return body
            End If

            Return rewriter.Rewrite()
        End Function

        Friend Overrides Function EnsureAllSymbolsAndSignature() As Boolean
            Dim hasErrors As Boolean = MyBase.EnsureAllSymbolsAndSignature

85
            If Me.Method.IsSub OrElse Me._elementType.IsErrorType Then
P
Pilchie 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
                hasErrors = True
            End If

            ' NOTE: in current implementation these attributes must exist
            ' TODO: change to "don't use if not found"
            EnsureWellKnownMember(Of MethodSymbol)(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor, hasErrors)
            EnsureWellKnownMember(Of MethodSymbol)(WellKnownMember.System_Diagnostics_DebuggerNonUserCodeAttribute__ctor, hasErrors)

            ' NOTE: We don't ensure DebuggerStepThroughAttribute, it is just not emitted if not found
            ' EnsureWellKnownMember(Of MethodSymbol)(WellKnownMember.System_Diagnostics_DebuggerStepThroughAttribute__ctor, hasErrors)

            ' TODO: do we need these here? They are used on the actual iterator method.
            ' EnsureWellKnownMember(Of MethodSymbol)(WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachine__ctor, hasErrors)

            EnsureSpecialType(SpecialType.System_Object, hasErrors)
            EnsureSpecialType(SpecialType.System_Boolean, hasErrors)
            EnsureSpecialType(SpecialType.System_Int32, hasErrors)

            If Me.Method.ReturnType.IsDefinition Then
105
                If Me._isEnumerable Then
P
Pilchie 已提交
106 107 108
                    EnsureSpecialType(SpecialType.System_Collections_IEnumerator, hasErrors)
                End If
            Else
109
                If Me._isEnumerable Then
P
Pilchie 已提交
110 111 112 113 114 115 116 117 118 119 120 121
                    EnsureSpecialType(SpecialType.System_Collections_Generic_IEnumerator_T, hasErrors)
                    EnsureSpecialType(SpecialType.System_Collections_IEnumerable, hasErrors)
                End If

                EnsureSpecialType(SpecialType.System_Collections_IEnumerator, hasErrors)
            End If

            EnsureSpecialType(SpecialType.System_IDisposable, hasErrors)

            Return hasErrors
        End Function

W
wochae 已提交
122 123 124 125
        Protected Overrides Sub GenerateControlFields()
            ' Add a field: int _state
            Me.StateField = Me.F.StateMachineField(Me.F.SpecialType(SpecialType.System_Int32), Me.Method, GeneratedNames.MakeStateMachineStateFieldName(), Accessibility.Public)

P
Pilchie 已提交
126
            ' Add a field: T current
127
            _currentField = F.StateMachineField(_elementType, Me.Method, GeneratedNames.MakeIteratorCurrentFieldName(), Accessibility.Public)
P
Pilchie 已提交
128 129

            ' if it is an Enumerable, add a field: initialThreadId As Integer
130
            _initialThreadIdField = If(_isEnumerable,
W
wochae 已提交
131
                F.StateMachineField(F.SpecialType(SpecialType.System_Int32), Me.Method, GeneratedNames.MakeIteratorInitialThreadIdName(), Accessibility.Public),
P
Pilchie 已提交
132 133 134 135 136 137 138 139
                Nothing)

        End Sub

        Protected Overrides Sub GenerateMethodImplementations()
            Dim managedThreadId As BoundExpression = Nothing  ' Thread.CurrentThread.ManagedThreadId

            ' Add bool IEnumerator.MoveNext() and void IDisposable.Dispose()
140
            Dim disposeMethod = Me.OpenMethodImplementation(SpecialMember.System_IDisposable__Dispose,
P
Pilchie 已提交
141 142 143
                                                             "Dispose",
                                                             DebugAttributes.DebuggerNonUserCodeAttribute,
                                                             Accessibility.Private,
144 145
                                                             generateDebugInfo:=False,
                                                             hasMethodBodyDependency:=True)
P
Pilchie 已提交
146

147 148 149
            Dim debuggerHidden = IsDebuggerHidden(Me.Method)
            Dim moveNextAttrs As DebugAttributes = DebugAttributes.CompilerGeneratedAttribute
            If debuggerHidden Then moveNextAttrs = moveNextAttrs Or DebugAttributes.DebuggerHiddenAttribute
150
            Dim moveNextMethod = Me.OpenMethodImplementation(SpecialMember.System_Collections_IEnumerator__MoveNext,
P
Pilchie 已提交
151
                                                             "MoveNext",
152
                                                             moveNextAttrs,
P
Pilchie 已提交
153
                                                             Accessibility.Private,
154 155
                                                             generateDebugInfo:=True,
                                                             hasMethodBodyDependency:=True)
P
Pilchie 已提交
156 157 158 159

            GenerateMoveNextAndDispose(moveNextMethod, disposeMethod)
            F.CurrentMethod = moveNextMethod

160
            If _isEnumerable Then
P
Pilchie 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174
                ' generate the code for GetEnumerator()
                '    IEnumerable<elementType> result;
                '    if (this.initialThreadId == Thread.CurrentThread.ManagedThreadId && this.state == -2)
                '    {
                '        this.state = 0;
                '        result = this;
                '    }
                '    else
                '    {
                '        result = new Ints0_Impl(0);
                '    }
                '    result.parameter = this.parameterProxy; ' copy all of the parameter proxies

                ' Add IEnumerator<int> IEnumerable<int>.GetEnumerator()
175
                Dim getEnumeratorGeneric = Me.OpenMethodImplementation(F.SpecialType(SpecialType.System_Collections_Generic_IEnumerable_T).Construct(_elementType),
P
Pilchie 已提交
176 177 178 179
                                                            SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator,
                                                            "GetEnumerator",
                                                            DebugAttributes.DebuggerNonUserCodeAttribute,
                                                            Accessibility.Private,
180 181
                                                            generateDebugInfo:=False,
                                                            hasMethodBodyDependency:=False)
P
Pilchie 已提交
182 183

                Dim bodyBuilder = ArrayBuilder(Of BoundStatement).GetInstance()
184
                Dim resultVariable = F.SynthesizedLocal(StateMachineType)      ' iteratorClass result;
P
Pilchie 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

                Dim currentManagedThreadIdMethod As MethodSymbol = Nothing

                Dim currentManagedThreadIdProperty As PropertySymbol = F.WellKnownMember(Of PropertySymbol)(WellKnownMember.System_Environment__CurrentManagedThreadId, isOptional:=True)

                If (currentManagedThreadIdProperty IsNot Nothing) Then
                    currentManagedThreadIdMethod = currentManagedThreadIdProperty.GetMethod()
                End If

                If (currentManagedThreadIdMethod IsNot Nothing) Then
                    managedThreadId = F.Call(Nothing, currentManagedThreadIdMethod)
                Else
                    managedThreadId = F.Property(F.Property(WellKnownMember.System_Threading_Thread__CurrentThread), WellKnownMember.System_Threading_Thread__ManagedThreadId)
                End If

                ' if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId)
                '    this.state = 0;
                '    result = this;
                '    goto thisInitialized
                ' else
                '    result = new IteratorClass(0)
                '    ' initialize [Me] if needed
                '    thisInitialized:
                '    ' initialize other fields
                Dim thisInitialized = F.GenerateLabel("thisInitialized")
                bodyBuilder.Add(
                    F.If(
                    condition:=
                        F.LogicalAndAlso(
214
                            F.IntEqual(F.Field(F.Me, StateField, False), F.Literal(StateMachineStates.FinishedStateMachine)),
215
                            F.IntEqual(F.Field(F.Me, _initialThreadIdField, False), managedThreadId)),
P
Pilchie 已提交
216 217
                    thenClause:=
                        F.Block(
218 219
                            F.Assignment(F.Field(F.Me, StateField, True), F.Literal(StateMachineStates.FirstUnusedState)),
                            F.Assignment(F.Local(resultVariable, True), F.Me),
P
Pilchie 已提交
220 221 222 223 224
                            If(Method.IsShared OrElse Method.MeParameter.Type.IsReferenceType,
                                    F.Goto(thisInitialized),
                                    DirectCast(F.Block(), BoundStatement))
                        ),
                    elseClause:=
225
                        F.Assignment(F.Local(resultVariable, True), F.[New](StateMachineType.Constructor, F.Literal(0)))
P
Pilchie 已提交
226 227 228 229
                    ))

                ' Initialize all the parameter copies
                Dim copySrc = InitialParameters
W
wochae 已提交
230
                Dim copyDest = nonReusableLocalProxies
P
Pilchie 已提交
231 232 233 234 235 236
                If Not Method.IsShared Then
                    ' starting with "this"
                    Dim proxy As FieldSymbol = Nothing
                    If (copyDest.TryGetValue(Method.MeParameter, proxy)) Then
                        bodyBuilder.Add(
                            F.Assignment(
237
                                F.Field(F.Local(resultVariable, True), proxy.AsMember(StateMachineType), True),
238
                                F.Field(F.Me, copySrc(Method.MeParameter).AsMember(F.CurrentType), False)))
P
Pilchie 已提交
239 240 241 242 243 244 245 246 247 248
                    End If
                End If

                bodyBuilder.Add(F.Label(thisInitialized))

                For Each parameter In Method.Parameters
                    Dim proxy As FieldSymbol = Nothing
                    If (copyDest.TryGetValue(parameter, proxy)) Then
                        bodyBuilder.Add(
                            F.Assignment(
249
                                F.Field(F.Local(resultVariable, True), proxy.AsMember(StateMachineType), True),
250
                                F.Field(F.Me, copySrc(parameter).AsMember(F.CurrentType), False)))
P
Pilchie 已提交
251 252 253 254 255 256 257 258 259 260 261 262
                    End If
                Next

                bodyBuilder.Add(F.Return(F.Local(resultVariable, False)))
                F.CloseMethod(F.Block(ImmutableArray.Create(resultVariable), bodyBuilder.ToImmutableAndFree()))

                ' Generate IEnumerable.GetEnumerator
                ' NOTE: this is a private implementing method. Its name is irrelevant
                '       but must be different from another GetEnumerator. Dev11 uses GetEnumerator0 here.
                '       IEnumerable.GetEnumerator seems better -
                '       it is clear why we have the property, and "Current" suffix will be shared in metadata with another Current.
                '       It is also consistent with the naming of IEnumerable.Current (see below).
263
                Me.OpenMethodImplementation(SpecialMember.System_Collections_IEnumerable__GetEnumerator,
P
Pilchie 已提交
264 265 266
                                            "IEnumerable.GetEnumerator",
                                            DebugAttributes.DebuggerNonUserCodeAttribute,
                                            Accessibility.Private,
267 268
                                            generateDebugInfo:=False,
                                            hasMethodBodyDependency:=False)
269
                F.CloseMethod(F.Return(F.Call(F.Me, getEnumeratorGeneric)))
P
Pilchie 已提交
270 271
            End If

272
            ' Add T IEnumerator<T>.Current
273
            Me.OpenPropertyImplementation(F.SpecialType(SpecialType.System_Collections_Generic_IEnumerator_T).Construct(_elementType),
274 275 276 277 278
                                          SpecialMember.System_Collections_Generic_IEnumerator_T__Current,
                                          "Current",
                                          DebugAttributes.DebuggerNonUserCodeAttribute,
                                          Accessibility.Private,
                                          False)
279
            F.CloseMethod(F.Return(F.Field(F.Me, _currentField, False)))
P
Pilchie 已提交
280

281
            ' Add void IEnumerator.Reset()
282 283 284 285
            Me.OpenMethodImplementation(SpecialMember.System_Collections_IEnumerator__Reset,
                                        "Reset",
                                        DebugAttributes.DebuggerNonUserCodeAttribute,
                                        Accessibility.Private,
286 287
                                        generateDebugInfo:=False,
                                        hasMethodBodyDependency:=False)
288
            F.CloseMethod(F.Throw(F.[New](F.WellKnownType(WellKnownType.System_NotSupportedException))))
P
Pilchie 已提交
289 290 291 292 293 294 295 296

            ' Add object IEnumerator.Current
            ' NOTE: this is a private implementing property. Its name is irrelevant
            '       but must be different from another Current.
            '       Dev11 uses fully qualified and substituted name here (System.Collections.Generic.IEnumerator(Of Object).Current), 
            '       It may be an overkill and may lead to metadata bloat. 
            '       IEnumerable.Current seems better -
            '       it is clear why we have the property, and "Current" suffix will be shared in metadata with another Current.
297 298 299 300 301
            Me.OpenPropertyImplementation(SpecialMember.System_Collections_IEnumerator__Current,
                                          "IEnumerator.Current",
                                          DebugAttributes.DebuggerNonUserCodeAttribute,
                                          Accessibility.Private,
                                          False)
302
            F.CloseMethod(F.Return(F.Field(F.Me, _currentField, False)))
303 304 305

            ' Add a body for the constructor
            If True Then
306
                F.CurrentMethod = StateMachineType.Constructor
307 308
                Dim bodyBuilder = ArrayBuilder(Of BoundStatement).GetInstance()
                bodyBuilder.Add(F.BaseInitialization())
309
                bodyBuilder.Add(F.Assignment(F.Field(F.Me, StateField, True), F.Parameter(F.CurrentMethod.Parameters(0)).MakeRValue))    ' this.state = state
P
Pilchie 已提交
310

311
                If _isEnumerable Then
312
                    ' this.initialThreadId = Thread.CurrentThread.ManagedThreadId;
313
                    bodyBuilder.Add(F.Assignment(F.Field(F.Me, _initialThreadIdField, True), managedThreadId))
P
Pilchie 已提交
314 315
                End If

316 317 318 319 320
                bodyBuilder.Add(F.Return())
                F.CloseMethod(F.Block(bodyBuilder.ToImmutableAndFree()))
                bodyBuilder = Nothing
            End If

P
Pilchie 已提交
321 322
        End Sub

W
wochae 已提交
323
        Protected Overrides Function GenerateStateMachineCreation(stateMachineVariable As LocalSymbol, frameType As NamedTypeSymbol) As BoundStatement
P
Pilchie 已提交
324 325 326 327 328 329
            Return F.Return(F.Local(stateMachineVariable, False))
        End Function

        Protected Overrides Sub InitializeStateMachine(bodyBuilder As ArrayBuilder(Of BoundStatement), frameType As NamedTypeSymbol, stateMachineLocal As LocalSymbol)
            ' Dim stateMachineLocal As new IteratorImplementationClass(N)
            ' where N is either 0 (if we're producing an enumerator) or -2 (if we're producing an enumerable)
330
            Dim initialState = If(_isEnumerable, StateMachineStates.FinishedStateMachine, StateMachineStates.FirstUnusedState)
P
Pilchie 已提交
331 332 333
            bodyBuilder.Add(
                F.Assignment(
                    F.Local(stateMachineLocal, True),
334
                    F.[New](StateMachineType.Constructor.AsMember(frameType), F.Literal(initialState))))
P
Pilchie 已提交
335 336
        End Sub

337
        Protected Overrides ReadOnly Property PreserveInitialParameterValues As Boolean
P
Pilchie 已提交
338
            Get
339
                Return Me._isEnumerable
P
Pilchie 已提交
340 341 342 343 344
            End Get
        End Property

        Friend Overrides ReadOnly Property TypeMap As TypeSubstitution
            Get
345
                Return StateMachineType.TypeSubstitution
P
Pilchie 已提交
346 347 348
            End Get
        End Property

349
        Private Sub GenerateMoveNextAndDispose(moveNextMethod As SynthesizedStateMachineMethod, disposeMethod As SynthesizedStateMachineMethod)
W
wochae 已提交
350 351 352
            Dim rewriter = New IteratorMethodToClassRewriter(method:=Me.Method,
                                                          F:=Me.F,
                                                          state:=Me.StateField,
353
                                                          current:=Me._currentField,
W
wochae 已提交
354 355 356 357 358 359
                                                          HoistedVariables:=Me.hoistedVariables,
                                                          localProxies:=Me.nonReusableLocalProxies,
                                                          SynthesizedLocalOrdinals:=Me.SynthesizedLocalOrdinals,
                                                          slotAllocatorOpt:=Me.SlotAllocatorOpt,
                                                          nextFreeHoistedLocalSlot:=Me.nextFreeHoistedLocalSlot,
                                                          diagnostics:=Diagnostics)
P
Pilchie 已提交
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

            rewriter.GenerateMoveNextAndDispose(Body, moveNextMethod, disposeMethod)
        End Sub

        Protected Overrides Function CreateByValLocalCapture(field As FieldSymbol, local As LocalSymbol) As FieldSymbol
            Return field
        End Function

        Protected Overrides Function CreateParameterCapture(field As FieldSymbol, parameter As ParameterSymbol) As FieldSymbol
            Return field
        End Function

        Protected Overrides Sub InitializeParameterWithProxy(parameter As ParameterSymbol, proxy As FieldSymbol, stateMachineVariable As LocalSymbol, initializers As ArrayBuilder(Of BoundExpression))
            Debug.Assert(proxy IsNot Nothing)

            Dim frameType As NamedTypeSymbol = If(Me.Method.IsGenericMethod,
376 377
                                                  Me.StateMachineType.Construct(Me.Method.TypeArguments),
                                                  Me.StateMachineType)
P
Pilchie 已提交
378 379

            Dim expression As BoundExpression = If(parameter.IsMe,
380
                                                   DirectCast(Me.F.Me, BoundExpression),
P
Pilchie 已提交
381 382 383 384
                                                   Me.F.Parameter(parameter).MakeRValue())
            initializers.Add(
                Me.F.AssignmentExpression(
                    Me.F.Field(
385
                        Me.F.Local(stateMachineVariable, True),
P
Pilchie 已提交
386 387 388 389 390 391 392
                        proxy.AsMember(frameType),
                        True),
                    expression))
        End Sub
    End Class
End Namespace