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

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 15 16 17 18 19

        Private ReadOnly elementType As TypeSymbol
        Private ReadOnly isEnumerable As Boolean

        Private currentField As FieldSymbol
        Private initialThreadIdField As FieldSymbol

        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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

            Me.isEnumerable = isEnumerable

            Dim methodReturnType = method.ReturnType
            If methodReturnType.GetArity = 0 Then
                Me.elementType = method.ContainingAssembly.GetSpecialType(SpecialType.System_Object)
            Else
                ' the element type may contain method type parameters, which are now alpha-renamed into type parameters of the generated class
                Me.elementType = DirectCast(methodReturnType, NamedTypeSymbol).TypeArgumentsNoUseSiteDiagnostics().Single().InternalSubstituteTypeParameters(Me.TypeMap)
            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 71 72

            If compilationState.ModuleBuilderOpt IsNot Nothing Then
                compilationState.ModuleBuilderOpt.CompilationState.SetStateMachineType(method, stateMachineType)
            End If
P
Pilchie 已提交
73

74
            Dim rewriter As New IteratorRewriter(body, method, isEnumerable, stateMachineType, slotAllocatorOpt, compilationState, diagnostics)
P
Pilchie 已提交
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

            ' 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

            If Me.Method.IsSub OrElse Me.elementType.IsErrorType Then
                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
                If Me.isEnumerable Then
                    EnsureSpecialType(SpecialType.System_Collections_IEnumerator, hasErrors)
                End If
            Else
                If Me.isEnumerable Then
                    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 已提交
124 125 126 127
        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 已提交
128
            ' Add a field: T current
W
wochae 已提交
129
            currentField = F.StateMachineField(elementType, Me.Method, GeneratedNames.MakeIteratorCurrentFieldName(), Accessibility.Public)
P
Pilchie 已提交
130 131 132

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

        End Sub

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

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

149 150 151
            Dim debuggerHidden = IsDebuggerHidden(Me.Method)
            Dim moveNextAttrs As DebugAttributes = DebugAttributes.CompilerGeneratedAttribute
            If debuggerHidden Then moveNextAttrs = moveNextAttrs Or DebugAttributes.DebuggerHiddenAttribute
152
            Dim moveNextMethod = Me.OpenMethodImplementation(SpecialMember.System_Collections_IEnumerator__MoveNext,
P
Pilchie 已提交
153
                                                             "MoveNext",
154
                                                             moveNextAttrs,
P
Pilchie 已提交
155
                                                             Accessibility.Private,
156 157
                                                             generateDebugInfo:=True,
                                                             hasMethodBodyDependency:=True)
P
Pilchie 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

            GenerateMoveNextAndDispose(moveNextMethod, disposeMethod)
            F.CurrentMethod = moveNextMethod

            If isEnumerable Then
                ' 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()
177
                Dim getEnumeratorGeneric = Me.OpenMethodImplementation(F.SpecialType(SpecialType.System_Collections_Generic_IEnumerable_T).Construct(elementType),
P
Pilchie 已提交
178 179 180 181
                                                            SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator,
                                                            "GetEnumerator",
                                                            DebugAttributes.DebuggerNonUserCodeAttribute,
                                                            Accessibility.Private,
182 183
                                                            generateDebugInfo:=False,
                                                            hasMethodBodyDependency:=False)
P
Pilchie 已提交
184 185

                Dim bodyBuilder = ArrayBuilder(Of BoundStatement).GetInstance()
186
                Dim resultVariable = F.SynthesizedLocal(StateMachineType)      ' iteratorClass result;
P
Pilchie 已提交
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 214 215

                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(
216 217
                            F.IntEqual(F.Field(F.Me, StateField, False), F.Literal(StateMachineStates.FinishedStateMachine)),
                            F.IntEqual(F.Field(F.Me, initialThreadIdField, False), managedThreadId)),
P
Pilchie 已提交
218 219
                    thenClause:=
                        F.Block(
220 221
                            F.Assignment(F.Field(F.Me, StateField, True), F.Literal(StateMachineStates.FirstUnusedState)),
                            F.Assignment(F.Local(resultVariable, True), F.Me),
P
Pilchie 已提交
222 223 224 225 226
                            If(Method.IsShared OrElse Method.MeParameter.Type.IsReferenceType,
                                    F.Goto(thisInitialized),
                                    DirectCast(F.Block(), BoundStatement))
                        ),
                    elseClause:=
227
                        F.Assignment(F.Local(resultVariable, True), F.[New](StateMachineType.Constructor, F.Literal(0)))
P
Pilchie 已提交
228 229 230 231
                    ))

                ' Initialize all the parameter copies
                Dim copySrc = InitialParameters
W
wochae 已提交
232
                Dim copyDest = nonReusableLocalProxies
P
Pilchie 已提交
233 234 235 236 237 238
                If Not Method.IsShared Then
                    ' starting with "this"
                    Dim proxy As FieldSymbol = Nothing
                    If (copyDest.TryGetValue(Method.MeParameter, proxy)) Then
                        bodyBuilder.Add(
                            F.Assignment(
239
                                F.Field(F.Local(resultVariable, True), proxy.AsMember(StateMachineType), True),
240
                                F.Field(F.Me, copySrc(Method.MeParameter).AsMember(F.CurrentType), False)))
P
Pilchie 已提交
241 242 243 244 245 246 247 248 249 250
                    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(
251
                                F.Field(F.Local(resultVariable, True), proxy.AsMember(StateMachineType), True),
252
                                F.Field(F.Me, copySrc(parameter).AsMember(F.CurrentType), False)))
P
Pilchie 已提交
253 254 255 256 257 258 259 260 261 262 263 264
                    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).
265
                Me.OpenMethodImplementation(SpecialMember.System_Collections_IEnumerable__GetEnumerator,
P
Pilchie 已提交
266 267 268
                                            "IEnumerable.GetEnumerator",
                                            DebugAttributes.DebuggerNonUserCodeAttribute,
                                            Accessibility.Private,
269 270
                                            generateDebugInfo:=False,
                                            hasMethodBodyDependency:=False)
271
                F.CloseMethod(F.Return(F.Call(F.Me, getEnumeratorGeneric)))
P
Pilchie 已提交
272 273
            End If

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

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

            ' 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.
299 300 301 302 303 304
            Me.OpenPropertyImplementation(SpecialMember.System_Collections_IEnumerator__Current,
                                          "IEnumerator.Current",
                                          DebugAttributes.DebuggerNonUserCodeAttribute,
                                          Accessibility.Private,
                                          False)
            F.CloseMethod(F.Return(F.Field(F.Me, currentField, False)))
305 306 307

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

313 314
                If isEnumerable Then
                    ' this.initialThreadId = Thread.CurrentThread.ManagedThreadId;
315
                    bodyBuilder.Add(F.Assignment(F.Field(F.Me, initialThreadIdField, True), managedThreadId))
P
Pilchie 已提交
316 317
                End If

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

P
Pilchie 已提交
323 324
        End Sub

W
wochae 已提交
325
        Protected Overrides Function GenerateStateMachineCreation(stateMachineVariable As LocalSymbol, frameType As NamedTypeSymbol) As BoundStatement
P
Pilchie 已提交
326 327 328 329 330 331 332 333 334 335
            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)
            Dim initialState = If(isEnumerable, StateMachineStates.FinishedStateMachine, StateMachineStates.FirstUnusedState)
            bodyBuilder.Add(
                F.Assignment(
                    F.Local(stateMachineLocal, True),
336
                    F.[New](StateMachineType.Constructor.AsMember(frameType), F.Literal(initialState))))
P
Pilchie 已提交
337 338
        End Sub

339
        Protected Overrides ReadOnly Property PreserveInitialParameterValues As Boolean
P
Pilchie 已提交
340 341 342 343 344 345 346
            Get
                Return Me.isEnumerable
            End Get
        End Property

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

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

            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,
378 379
                                                  Me.StateMachineType.Construct(Me.Method.TypeArguments),
                                                  Me.StateMachineType)
P
Pilchie 已提交
380 381

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