/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.vb
520 строк
28 KB
Jan Jones
Validate pooled objects in compiler tests (#82784)
15 май 2026, 09:55
Не верифицирован
15 май 2026, 09:55
e141f0d
Код
Авторство
О чём код?
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.CodeGen Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic ''' <summary> ''' </summary> ''' <typeparam name="TProxy"> ''' Type used by State Machine rewriter to represent symbol proxy. Lambda rewriter as ''' well as iterator rewriter use simplified form of proxy as they only capture ''' locals as r-values in fields, async rewriter uses a different structure as a proxy ''' because it has to capture l-values on stack as well ''' </typeparam> Partial Friend MustInherit Class StateMachineRewriter(Of TProxy) Protected ReadOnly Body As BoundStatement Protected ReadOnly Method As MethodSymbol Protected ReadOnly Diagnostics As BindingDiagnosticBag Protected ReadOnly F As SyntheticBoundNodeFactory Protected ReadOnly StateMachineType As SynthesizedContainer Protected ReadOnly SlotAllocatorOpt As VariableSlotAllocator Protected ReadOnly SynthesizedLocalOrdinals As SynthesizedLocalOrdinalsDispenser Protected ReadOnly StateDebugInfoBuilder As ArrayBuilder(Of StateMachineStateDebugInfo) Protected StateField As FieldSymbol Protected nonReusableLocalProxies As Dictionary(Of Symbol, TProxy) Protected nextFreeHoistedLocalSlot As Integer Protected hoistedVariables As IReadOnlySet(Of Symbol) Protected InitialParameters As Dictionary(Of Symbol, TProxy) Protected Sub New(body As BoundStatement, method As MethodSymbol, stateMachineType As StateMachineTypeSymbol, stateMachineStateDebugInfoBuilder As ArrayBuilder(Of StateMachineStateDebugInfo), slotAllocatorOpt As VariableSlotAllocator, compilationState As TypeCompilationState, diagnostics As BindingDiagnosticBag) Debug.Assert(body IsNot Nothing) Debug.Assert(method IsNot Nothing) Debug.Assert(compilationState IsNot Nothing) Debug.Assert(diagnostics IsNot Nothing) Debug.Assert(diagnostics.AccumulatesDiagnostics) Debug.Assert(stateMachineType IsNot Nothing) Me.Body = body Me.Method = method Me.StateMachineType = stateMachineType Me.SlotAllocatorOpt = slotAllocatorOpt Me.Diagnostics = diagnostics Me.SynthesizedLocalOrdinals = New SynthesizedLocalOrdinalsDispenser() Me.StateDebugInfoBuilder = stateMachineStateDebugInfoBuilder Me.nonReusableLocalProxies = New Dictionary(Of Symbol, TProxy)() Me.F = New SyntheticBoundNodeFactory(method, method, method.ContainingType, body.Syntax, compilationState, diagnostics) End Sub ''' <summary> ''' True if the initial values of locals in the rewritten method need to be preserved. (e.g. enumerable iterator methods) ''' </summary> Protected MustOverride ReadOnly Property PreserveInitialParameterValues As Boolean ''' <summary> ''' Type substitution if applicable or Nothing ''' </summary> Friend MustOverride ReadOnly Property TypeMap As TypeSubstitution ''' <summary> ''' Add fields to the state machine class that control the state machine. ''' </summary> Protected MustOverride Sub GenerateControlFields() ''' <summary> ''' Initialize the state machine class. ''' </summary> Protected MustOverride Sub InitializeStateMachine(bodyBuilder As ArrayBuilder(Of BoundStatement), frameType As NamedTypeSymbol, stateMachineLocal As LocalSymbol) ''' <summary> ''' Generate implementation-specific state machine initialization for the kickoff method body. ''' </summary> Protected MustOverride Function GenerateStateMachineCreation(stateMachineVariable As LocalSymbol, frameType As NamedTypeSymbol) As BoundStatement ''' <summary> ''' Generate implementation-specific state machine member method implementations. ''' </summary> Protected MustOverride Sub GenerateMethodImplementations() Protected Function Rewrite() As BoundBlock Debug.Assert(Not Me.PreserveInitialParameterValues OrElse Method.IsIterator) Me.F.OpenNestedType(Me.StateMachineType) Me.F.CompilationState.StateMachineImplementationClass(Me.Method) = Me.StateMachineType Me.GenerateControlFields() ' and fields for the initial values of all the parameters of the method If Me.PreserveInitialParameterValues Then Me.InitialParameters = New Dictionary(Of Symbol, TProxy)() End If ' add fields for the captured variables of the method Dim variablesToHoist = IteratorAndAsyncCaptureWalker.Analyze(New FlowAnalysisInfo(F.CompilationState.Compilation, Me.Method, Me.Body), Me.Diagnostics.DiagnosticBag) CreateNonReusableLocalProxies(variablesToHoist, Me.nextFreeHoistedLocalSlot) Me.hoistedVariables = New OrderedSet(Of Symbol)(variablesToHoist.CapturedLocals) GenerateMethodImplementations() SynthesizedLocalOrdinals.Free() ' Return a replacement body for the kickoff method Return GenerateKickoffMethodBody() End Function Private Function GenerateKickoffMethodBody() As BoundBlock Me.F.CurrentMethod = Me.Method Dim bodyBuilder = ArrayBuilder(Of BoundStatement).GetInstance() bodyBuilder.Add(SyntheticBoundNodeFactory.HiddenSequencePoint()) Dim frameType As NamedTypeSymbol = If(Me.Method.IsGenericMethod, Me.StateMachineType.Construct(Method.TypeArguments), Me.StateMachineType) Dim stateMachineVariable As LocalSymbol = F.SynthesizedLocal(frameType) InitializeStateMachine(bodyBuilder, frameType, stateMachineVariable) ' Plus code to initialize all of the parameter proxies result Dim proxies = If(PreserveInitialParameterValues, Me.InitialParameters, Me.nonReusableLocalProxies) Dim initializers = ArrayBuilder(Of BoundExpression).GetInstance() ' starting with the "Me" proxy If Not Me.Method.IsShared AndAlso Me.Method.MeParameter IsNot Nothing Then Dim proxy As TProxy = Nothing If proxies.TryGetValue(Me.Method.MeParameter, proxy) Then InitializeParameterWithProxy(Me.Method.MeParameter, proxy, stateMachineVariable, initializers) End If End If ' then all the parameters For Each parameter In Me.Method.Parameters Dim proxy As TProxy = Nothing If proxies.TryGetValue(parameter, proxy) Then InitializeParameterWithProxy(parameter, proxy, stateMachineVariable, initializers) End If Next If initializers.Count > 0 Then bodyBuilder.Add(F.ExpressionStatement(F.Sequence(initializers.ToArray()))) End If initializers.Free() bodyBuilder.Add(GenerateStateMachineCreation(stateMachineVariable, frameType)) Return Me.F.Block( ImmutableArray.Create(Of LocalSymbol)(stateMachineVariable), bodyBuilder.ToImmutableAndFree()) End Function Private Sub CreateNonReusableLocalProxies(captured As IteratorAndAsyncCaptureWalker.Result, ByRef nextFreeHoistedLocalSlot As Integer) Dim typeMap As TypeSubstitution = StateMachineType.TypeSubstitution Dim isDebugBuild As Boolean = F.Compilation.Options.OptimizationLevel = OptimizationLevel.Debug Dim mapToPreviousFields = isDebugBuild AndAlso SlotAllocatorOpt IsNot Nothing Me.nextFreeHoistedLocalSlot = If(mapToPreviousFields, SlotAllocatorOpt.PreviousHoistedLocalSlotCount, 0) For Each variable In captured.CapturedLocals Debug.Assert(variable.Kind = SymbolKind.Local OrElse variable.Kind = SymbolKind.Parameter) Select Case variable.Kind Case SymbolKind.Local Dim local = DirectCast(variable, LocalSymbol) ' No need to hoist constants If local.IsConst Then Continue For End If If local.SynthesizedKind = SynthesizedLocalKind.ConditionalBranchDiscriminator Then Continue For End If CaptureLocalSymbol(typeMap, DirectCast(variable, LocalSymbol), captured.ByRefLocalsInitializers) Case SymbolKind.Parameter CaptureParameterSymbol(typeMap, DirectCast(variable, ParameterSymbol)) End Select Next End Sub Protected Function CaptureParameterSymbol(typeMap As TypeSubstitution, parameter As ParameterSymbol) As TProxy Dim proxy As TProxy = Nothing If Me.nonReusableLocalProxies.TryGetValue(parameter, proxy) Then ' This proxy may have already be added while processing ' previous ByRef local Return proxy End If If parameter.IsMe Then Dim typeName As String = parameter.ContainingSymbol.ContainingType.Name Dim isMeOfClosureType As Boolean = typeName.StartsWith(GeneratedNameConstants.DisplayClassPrefix, StringComparison.Ordinal) ' NOTE: even though 'Me' is 'ByRef' in structures, Dev11 does capture it by value ' NOTE: without generation of any errors/warnings. Roslyn has to match this behavior proxy = CreateParameterCapture( Me.F.StateMachineField( Method.ContainingType, Me.Method, If(isMeOfClosureType, GeneratedNames.MakeStateMachineCapturedClosureMeName(typeName), GeneratedNames.MakeStateMachineCapturedMeName()), Accessibility.Friend), parameter) Me.nonReusableLocalProxies.Add(parameter, proxy) If Me.PreserveInitialParameterValues Then Dim initialMe As TProxy = If(Me.Method.ContainingType.IsStructureType(), CreateParameterCapture( Me.F.StateMachineField( Me.Method.ContainingType, Me.Method, GeneratedNames.MakeIteratorParameterProxyName(GeneratedNames.MakeStateMachineCapturedMeName()), Accessibility.Friend), parameter), Me.nonReusableLocalProxies(parameter)) Me.InitialParameters.Add(parameter, initialMe) End If Else Dim paramType As TypeSymbol = parameter.Type.InternalSubstituteTypeParameters(typeMap).Type Debug.Assert(Not parameter.IsByRef) proxy = CreateParameterCapture( F.StateMachineField( paramType, parameter, GeneratedNames.MakeStateMachineParameterName(parameter.Name), Accessibility.Friend), parameter) Me.nonReusableLocalProxies.Add(parameter, proxy) If Me.PreserveInitialParameterValues Then Me.InitialParameters.Add(parameter, CreateParameterCapture( Me.F.StateMachineField( paramType, parameter, GeneratedNames.MakeIteratorParameterProxyName(parameter.Name), Accessibility.Friend), parameter)) End If End If Return proxy End Function Protected Function CaptureLocalSymbol(typeMap As TypeSubstitution, local As LocalSymbol, initializers As Dictionary(Of LocalSymbol, BoundExpression)) As TProxy Dim proxy As TProxy = Nothing If nonReusableLocalProxies.TryGetValue(local, proxy) Then ' This proxy may have already be added while processing ' previous ByRef local Return proxy End If If local.IsByRef Then ' We'll create proxies for these variable later: ' TODO: so, we have to check if it is already in or not. See the early impl. Debug.Assert(initializers.ContainsKey(local)) proxy = CreateByRefLocalCapture(typeMap, local, initializers) nonReusableLocalProxies.Add(local, proxy) Return proxy End If ' Variable needs to be hoisted. Dim fieldType = local.Type.InternalSubstituteTypeParameters(typeMap).Type Dim id As LocalDebugId = LocalDebugId.None Dim slotIndex As Integer = -1 If Not local.SynthesizedKind.IsSlotReusable(F.Compilation.Options.OptimizationLevel) Then ' Calculate local debug id ' ' EnC: When emitting the baseline (gen 0) the id is stored in a custom debug information attached to the kickoff method. ' When emitting a delta the id is only used to map to the existing field in the previous generation. Dim declaratorSyntax As SyntaxNode = local.GetDeclaratorSyntax() Dim syntaxOffset As Integer = Me.Method.CalculateLocalSyntaxOffset(declaratorSyntax.SpanStart, declaratorSyntax.SyntaxTree) Dim ordinal As Integer = SynthesizedLocalOrdinals.AssignLocalOrdinal(local.SynthesizedKind, syntaxOffset) id = New LocalDebugId(syntaxOffset, ordinal) Dim previousSlotIndex = -1 If SlotAllocatorOpt IsNot Nothing AndAlso SlotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex(declaratorSyntax, F.CompilationState.ModuleBuilderOpt.Translate(fieldType, declaratorSyntax, Diagnostics.DiagnosticBag), local.SynthesizedKind, id, Diagnostics.DiagnosticBag, previousSlotIndex) Then slotIndex = previousSlotIndex End If End If If slotIndex = -1 Then slotIndex = Me.nextFreeHoistedLocalSlot Me.nextFreeHoistedLocalSlot = Me.nextFreeHoistedLocalSlot + 1 End If proxy = CreateByValLocalCapture(MakeHoistedFieldForLocal(local, fieldType, slotIndex, id), local) nonReusableLocalProxies.Add(local, proxy) Return proxy End Function Protected MustOverride Sub InitializeParameterWithProxy(parameter As ParameterSymbol, proxy As TProxy, stateMachineVariable As LocalSymbol, initializers As ArrayBuilder(Of BoundExpression)) Protected MustOverride Function CreateByValLocalCapture(field As FieldSymbol, local As LocalSymbol) As TProxy Protected MustOverride Function CreateParameterCapture(field As FieldSymbol, parameter As ParameterSymbol) As TProxy Protected Overridable Function CreateByRefLocalCapture(typeMap As TypeSubstitution, local As LocalSymbol, initializers As Dictionary(Of LocalSymbol, BoundExpression)) As TProxy ' This is only supposed to be reachable in Async rewriter Throw ExceptionUtilities.Unreachable End Function Protected Function MakeHoistedFieldForLocal(local As LocalSymbol, localType As TypeSymbol, slotIndex As Integer, id As LocalDebugId) As FieldSymbol Dim proxyName As String Select Case local.SynthesizedKind Case SynthesizedLocalKind.LambdaDisplayClass proxyName = GeneratedNameConstants.StateMachineHoistedUserVariableOrDisplayClassPrefix & GeneratedNameConstants.ClosureVariablePrefix & "$" & slotIndex Case SynthesizedLocalKind.UserDefined proxyName = GeneratedNameConstants.StateMachineHoistedUserVariableOrDisplayClassPrefix & local.Name & "$" & slotIndex Case SynthesizedLocalKind.With proxyName = GeneratedNameConstants.HoistedWithLocalPrefix & slotIndex Case Else proxyName = GeneratedNameConstants.HoistedSynthesizedLocalPrefix & slotIndex End Select Return F.StateMachineField(localType, Me.Method, proxyName, New LocalSlotDebugInfo(local.SynthesizedKind, id), slotIndex, Accessibility.Friend) End Function ''' <summary> ''' If any required special/well-known type/member is not found or has use-site errors ''' we should not continue with transformation because it may have unwanted consequences; ''' e.g. we do return Nothing if well-known member symbol is not found. This method should ''' check all required symbols and return False if any of them are missing or have use-site errors. ''' We will also return True if signature is definitely bad - contains parameters that are ByRef or have error types ''' </summary> Friend Overridable Function EnsureAllSymbolsAndSignature() As Boolean If Me.Method.ReturnType.IsErrorType Then Return True End If For Each parameter In Me.Method.Parameters If parameter.IsByRef OrElse parameter.Type.IsErrorType Then Return True End If Next Return False End Function Friend Function EnsureSpecialType(type As SpecialType, bag As BindingDiagnosticBag) As Symbol Return Binder.GetSpecialType(F.Compilation, type, Me.Body.Syntax, bag) End Function Friend Function EnsureWellKnownType(type As WellKnownType, bag As BindingDiagnosticBag) As Symbol Return Binder.GetWellKnownType(F.Compilation, type, Me.Body.Syntax, bag) End Function Friend Function EnsureSpecialMember(member As SpecialMember, bag As BindingDiagnosticBag) As Symbol Return Binder.GetSpecialTypeMember(F.Compilation.Assembly, member, Me.Body.Syntax, bag) End Function Friend Function EnsureWellKnownMember(member As WellKnownMember, bag As BindingDiagnosticBag) As Symbol Return Binder.GetWellKnownTypeMember(F.Compilation, member, Me.Body.Syntax, bag) End Function ''' <summary> ''' Check that the property and its getter exist and collect any use-site errors. ''' </summary> Friend Sub EnsureSpecialPropertyGetter(member As SpecialMember, bag As BindingDiagnosticBag) Dim symbol = DirectCast(EnsureSpecialMember(member, bag), PropertySymbol) If symbol IsNot Nothing Then Dim getter = symbol.GetMethod If getter Is Nothing Then Binder.ReportDiagnostic(bag, Body.Syntax, ERRID.ERR_NoGetProperty1, CustomSymbolDisplayFormatter.QualifiedName(symbol)) Return End If Dim useSiteInfo = getter.GetUseSiteInfo() Binder.ReportUseSite(bag, Body.Syntax, useSiteInfo) End If End Sub Friend Function OpenMethodImplementation(interfaceMethod As WellKnownMember, name As String, accessibility As Accessibility, Optional hasMethodBodyDependency As Boolean = False, Optional associatedProperty As PropertySymbol = Nothing) As SynthesizedMethod Dim methodToImplement As MethodSymbol = Me.F.WellKnownMember(Of MethodSymbol)(interfaceMethod) Return OpenMethodImplementation(methodToImplement, name, accessibility, hasMethodBodyDependency, associatedProperty) End Function Friend Function OpenMethodImplementation(interfaceMethod As SpecialMember, name As String, accessibility As Accessibility, Optional hasMethodBodyDependency As Boolean = False, Optional associatedProperty As PropertySymbol = Nothing) As SynthesizedMethod Dim methodToImplement As MethodSymbol = DirectCast(Me.F.SpecialMember(interfaceMethod), MethodSymbol) Return OpenMethodImplementation(methodToImplement, name, accessibility, hasMethodBodyDependency, associatedProperty) End Function Friend Function OpenMethodImplementation(interfaceType As NamedTypeSymbol, interfaceMethod As SpecialMember, name As String, accessibility As Accessibility, Optional hasMethodBodyDependency As Boolean = False, Optional associatedProperty As PropertySymbol = Nothing) As SynthesizedMethod Dim methodToImplement As MethodSymbol = DirectCast(Me.F.SpecialMember(interfaceMethod), MethodSymbol).AsMember(interfaceType) Return OpenMethodImplementation(methodToImplement, name, accessibility, hasMethodBodyDependency, associatedProperty) End Function Private Function OpenMethodImplementation(methodToImplement As MethodSymbol, methodName As String, accessibility As Accessibility, Optional hasMethodBodyDependency As Boolean = False, Optional associatedProperty As PropertySymbol = Nothing) As SynthesizedMethod ' Errors must be reported before and if any this point should not be reachable Debug.Assert(methodToImplement IsNot Nothing AndAlso methodToImplement.GetUseSiteInfo().DiagnosticInfo Is Nothing) Dim result As New SynthesizedStateMachineDebuggerNonUserCodeMethod(DirectCast(Me.F.CurrentType, StateMachineTypeSymbol), methodName, methodToImplement, Me.F.Syntax, accessibility, hasMethodBodyDependency, associatedProperty) Me.F.AddMethod(Me.F.CurrentType, result) Me.F.CurrentMethod = result Return result End Function Friend Function OpenPropertyImplementation(interfaceProperty As SpecialMember, name As String, accessibility As Accessibility) As MethodSymbol Dim methodToImplement As MethodSymbol = DirectCast(Me.F.SpecialMember(interfaceProperty), PropertySymbol).GetMethod Return OpenPropertyImplementation(methodToImplement, name, accessibility) End Function Friend Function OpenPropertyImplementation(interfaceType As NamedTypeSymbol, interfaceMethod As SpecialMember, name As String, accessibility As Accessibility) As MethodSymbol Dim methodToImplement As MethodSymbol = DirectCast(Me.F.SpecialMember(interfaceMethod), PropertySymbol).GetMethod.AsMember(interfaceType) Return OpenPropertyImplementation(methodToImplement, name, accessibility) End Function Private Function OpenPropertyImplementation(getterToImplement As MethodSymbol, name As String, accessibility As Accessibility) As MethodSymbol Dim prop As New SynthesizedStateMachineProperty(DirectCast(Me.F.CurrentType, StateMachineTypeSymbol), name, getterToImplement, Me.F.Syntax, accessibility) Me.F.AddProperty(Me.F.CurrentType, prop) Dim getter = prop.GetMethod Me.F.AddMethod(Me.F.CurrentType, getter) Me.F.CurrentMethod = getter Return getter End Function Friend Sub CloseMethod(body As BoundStatement) Me.F.CloseMethod(RewriteBodyIfNeeded(body, Me.F.TopLevelMethod, Me.F.CurrentMethod)) End Sub Friend Overridable Function RewriteBodyIfNeeded(body As BoundStatement, topMethod As MethodSymbol, currentMethod As MethodSymbol) As BoundStatement Return body End Function Friend Function OpenMoveNextMethodImplementation(interfaceMethod As WellKnownMember, accessibility As Accessibility) As SynthesizedMethod Dim methodToImplement As MethodSymbol = Me.F.WellKnownMember(Of MethodSymbol)(interfaceMethod) Return OpenMoveNextMethodImplementation(methodToImplement, accessibility) End Function Friend Function OpenMoveNextMethodImplementation(interfaceMethod As SpecialMember, accessibility As Accessibility) As SynthesizedMethod Dim methodToImplement As MethodSymbol = DirectCast(Me.F.SpecialMember(interfaceMethod), MethodSymbol) Return OpenMoveNextMethodImplementation(methodToImplement, accessibility) End Function Private Function OpenMoveNextMethodImplementation(methodToImplement As MethodSymbol, accessibility As Accessibility) As SynthesizedMethod ' Errors must be reported before and if any this point should not be reachable Debug.Assert(methodToImplement IsNot Nothing AndAlso methodToImplement.GetUseSiteInfo().DiagnosticInfo Is Nothing) Dim result As New SynthesizedStateMachineMoveNextMethod(DirectCast(Me.F.CurrentType, StateMachineTypeSymbol), methodToImplement, Me.F.Syntax, accessibility) Me.F.AddMethod(Me.F.CurrentType, result) Me.F.CurrentMethod = result Return result End Function End Class End Namespace