/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Lowering/LocalRewriter/LocalRewriter_Using.vb
269 строк
14 KB
Tomáš Matoušek
EnC: Allow adding/removing await expressions and yield statements in VB (#61569)
27 июн 2022, 21:08
Не верифицирован
27 июн 2022, 21:08
6f514e4
Код
Авторство
О чём код?
' 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.Diagnostics Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports TypeKind = Microsoft.CodeAnalysis.TypeKind Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend NotInheritable Class LocalRewriter ''' <summary> ''' A using statement of the form: ''' using Expression ''' list_of_statements ''' end using ''' ''' will be rewritten into: ''' ''' temp = Expression ''' Try ''' list_of_statements ''' Finally ''' If Temp IsNot Nothing Then ''' CType(temp, IDisposable).Dispose() ''' End If ''' End Try ''' ''' when the resource is a using locally declared variable no temporary is generated but the variable is read-only ''' A using statement of the form: ''' Using v As New MyDispose() ''' list_of_statements ''' End Using ''' ''' is rewritten to: ''' ''' Dim v As New MyDispose() ''' Try ''' list_of_statements ''' Finally ''' If v IsNot Nothing Then ''' CType(v, IDisposable).Dispose() ''' End If ''' End Try ''' ''' A using with multiple variable resources are equivalent to a nested using statement. ''' So a using statement of the form: ''' Using v1 As New MyDispose(), v2 As myDispose = New MyDispose() ''' list_of_statements ''' end using ''' ''' is rewritten to: ''' Dim v1 As New MyDispose ''' Try ''' Dim v2 As MyDispose = new MyDispose() ''' Try ''' list_of_statements ''' Finally ''' If v2 IsNot Nothing Then ''' CType(v2, IDisposable).Dispose() ''' End If ''' End Try ''' Finally ''' If v1 IsNot Nothing Then ''' CType(v1, IDisposable).Dispose() ''' End If ''' end try '''</summary> Public Overrides Function VisitUsingStatement(node As BoundUsingStatement) As BoundNode Dim saveState As UnstructuredExceptionHandlingContext = LeaveUnstructuredExceptionHandlingContext(node) Dim blockSyntax = DirectCast(node.Syntax, UsingBlockSyntax) ' rewrite the original using body only once here. Dim currentBody = DirectCast(Visit(node.Body), BoundBlock) Dim locals As ImmutableArray(Of LocalSymbol) = node.Locals Dim placeholderInfo As ValueTuple(Of BoundRValuePlaceholder, BoundExpression, BoundExpression) ' the initialization expressions (variable declaration & expression case) will be rewritten in ' "RewriteToInnerTryConstructForVariableDeclaration" to avoid code duplication If Not node.ResourceList.IsDefault Then ' Case "Using <variable declarations>" ' the try statements will be nested. To avoid re-rewriting we're iterating through the resource list in reverse For declarationIndex = node.ResourceList.Length - 1 To 0 Step -1 Dim localDeclaration = node.ResourceList(declarationIndex) If localDeclaration.Kind = BoundKind.LocalDeclaration Then Dim localVariableDeclaration = DirectCast(localDeclaration, BoundLocalDeclaration) placeholderInfo = node.UsingInfo.PlaceholderInfo(localVariableDeclaration.LocalSymbol.Type) currentBody = RewriteSingleUsingToTryFinally(node, declarationIndex, localDeclaration.Syntax, localVariableDeclaration.LocalSymbol, localVariableDeclaration.InitializerOpt, placeholderInfo, currentBody) Else Dim localAsNewDeclaration = DirectCast(localDeclaration, BoundAsNewLocalDeclarations) Dim variableCount = localAsNewDeclaration.LocalDeclarations.Length placeholderInfo = node.UsingInfo.PlaceholderInfo(localAsNewDeclaration.LocalDeclarations.First.LocalSymbol.Type) For initializedVariableIndex = localAsNewDeclaration.LocalDeclarations.Length - 1 To 0 Step -1 currentBody = RewriteSingleUsingToTryFinally(node, declarationIndex, localDeclaration.Syntax, localAsNewDeclaration.LocalDeclarations(initializedVariableIndex).LocalSymbol, localAsNewDeclaration.Initializer, placeholderInfo, currentBody) Next End If Next Else ' Case "Using <expression>" Debug.Assert(node.ResourceExpressionOpt IsNot Nothing) Dim initializationExpression = node.ResourceExpressionOpt placeholderInfo = node.UsingInfo.PlaceholderInfo(initializationExpression.Type) Dim tempResourceSymbol As LocalSymbol = New SynthesizedLocal(Me._currentMethodOrLambda, initializationExpression.Type, SynthesizedLocalKind.Using, blockSyntax.UsingStatement) currentBody = RewriteSingleUsingToTryFinally(node, resourceIndex:=0, ' There is only one resource - the expression resourceSyntax:=blockSyntax, tempResourceSymbol, initializationExpression, placeholderInfo, currentBody) locals = locals.Add(tempResourceSymbol) End If RestoreUnstructuredExceptionHandlingContext(node, saveState) Dim statements As ImmutableArray(Of BoundStatement) = currentBody.Statements If ShouldGenerateUnstructuredExceptionHandlingResumeCode(node) Then statements = RegisterUnstructuredExceptionHandlingResumeTarget(node.Syntax, canThrow:=True).Concat(statements) End If currentBody = New BoundBlock(node.Syntax, currentBody.StatementListSyntax, locals, statements) Dim prologue As BoundStatement = Nothing If Instrument(node) Then ' create a sequence point that contains the whole using statement as the first reachable sequence point ' of the using statement. The resource variables are not yet in scope. prologue = _instrumenterOpt.CreateUsingStatementPrologue(node) End If If prologue IsNot Nothing Then Return New BoundStatementList(node.UsingInfo.UsingStatementSyntax, ImmutableArray.Create(Of BoundStatement)(prologue, currentBody)) Else Return New BoundStatementList(node.UsingInfo.UsingStatementSyntax, ImmutableArray.Create(Of BoundStatement)(currentBody)) End If End Function ''' <summary> ''' Creates a TryFinally Statement for the given resource. ''' ''' This method creates the following for the arguments: ''' <localSymbol> = <initializationExpression> ''' Try ''' <currentBody> ''' Finally ''' If <disposeCondition> Then ''' <disposeConversion>.Dispose() ''' End If ''' End Try ''' ''' Note: this is used for both kinds of using statements (resource locals and resource expressions). ''' ''' </summary> ''' <returns>The new bound block containing the assignment of the initialization and the try/finally statement with ''' the passed body.</returns> Private Function RewriteSingleUsingToTryFinally( node As BoundUsingStatement, resourceIndex As Integer, resourceSyntax As SyntaxNode, localSymbol As LocalSymbol, initializationExpression As BoundExpression, ByRef placeholderInfo As ValueTuple(Of BoundRValuePlaceholder, BoundExpression, BoundExpression), currentBody As BoundBlock ) As BoundBlock Dim resourceType = localSymbol.Type Dim boundResourceLocal As BoundLocal = New BoundLocal(resourceSyntax, localSymbol, isLValue:=True, type:=resourceType) Dim resourcePlaceholder As BoundRValuePlaceholder = placeholderInfo.Item1 Dim disposeConversion As BoundExpression = placeholderInfo.Item2 Dim disposeCondition As BoundExpression = placeholderInfo.Item3 AddPlaceholderReplacement(resourcePlaceholder, boundResourceLocal.MakeRValue()) ' add a sequence point to stop on the "End Using" statement ' because there are a lot of hidden sequence points between the dispose call and the "end using" in Roslyn ' (caused by emitting try catch), we need to add a sequence point after each call with the syntax of the end using ' to match the Dev10 debugging experience. Dim newBody = DirectCast(Concat(currentBody, SyntheticBoundNodeFactory.HiddenSequencePoint()), BoundBlock) ' assign initialization to variable Dim boundResourceInitializationAssignment As BoundStatement = New BoundAssignmentOperator(resourceSyntax, boundResourceLocal, VisitAndGenerateObjectCloneIfNeeded(initializationExpression, suppressObjectClone:=True), suppressObjectClone:=True, type:=resourceType).ToStatement Dim instrument As Boolean = Me.Instrument(node) If instrument Then boundResourceInitializationAssignment = _instrumenterOpt.InstrumentUsingStatementResourceCapture(node, resourceIndex, boundResourceInitializationAssignment) End If ' create if statement with dispose call Dim disposeCall = GenerateDisposeCallForForeachAndUsing(resourceSyntax, boundResourceLocal, VisitExpressionNode(disposeCondition), True, VisitExpressionNode(disposeConversion)) Dim disposePrologue As BoundStatement = Nothing If instrument Then ' The block should start with a sequence point that points to the "End Using" statement. This is required in order to ' highlight the end using when someone step next after the last statement of the original body and in case an exception ' was thrown. disposePrologue = _instrumenterOpt.CreateUsingStatementDisposePrologue(node) End If Dim finallyStatements As ImmutableArray(Of BoundStatement) If disposePrologue IsNot Nothing Then finallyStatements = ImmutableArray.Create(disposePrologue, disposeCall) Else finallyStatements = ImmutableArray.Create(disposeCall) End If ' create finally block from the dispose call Dim finallyBlock = New BoundBlock(resourceSyntax, statementListSyntax:=Nothing, locals:=ImmutableArray(Of LocalSymbol).Empty, finallyStatements) ' rewrite try/finally block Dim tryFinally = RewriteTryStatement(resourceSyntax, newBody, ImmutableArray(Of BoundCatchBlock).Empty, finallyBlock, exitLabelOpt:=Nothing) newBody = New BoundBlock(resourceSyntax, statementListSyntax:=Nothing, locals:=ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(boundResourceInitializationAssignment, tryFinally)) RemovePlaceholderReplacement(resourcePlaceholder) Return newBody End Function End Class End Namespace