/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Analysis/InitializerRewriter.vb
285 строк
15 KB
Jonathon Marolf
update headers
23 янв 2020, 04:01
Не верифицирован
23 янв 2020, 04:01
91571a3
Код
Авторство
О чём код?
' 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.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic ''' <summary> ''' Turns the bound initializers into a list of bound assignment statements ''' </summary> Friend Module InitializerRewriter ''' <summary> ''' Builds a constructor body. ''' </summary> ''' <remarks> ''' Lowers initializers to fields assignments if not lowered yet and the first statement of the body isn't ''' a call to another constructor of the containing class. ''' </remarks> ''' <returns> ''' Bound block including ''' - call to a base constructor ''' - field initializers and top-level code ''' - remaining constructor statements (empty for a submission) ''' </returns> Friend Function BuildConstructorBody( compilationState As TypeCompilationState, constructorMethod As MethodSymbol, constructorInitializerOpt As BoundStatement, processedInitializers As Binder.ProcessedFieldOrPropertyInitializers, block As BoundBlock) As BoundBlock Dim hasMyBaseConstructorCall As Boolean = False Dim containingType = constructorMethod.ContainingType If HasExplicitMeConstructorCall(block, containingType, hasMyBaseConstructorCall) AndAlso Not hasMyBaseConstructorCall Then Return block End If ' rewrite initializers just once, statements will be reused when emitting all constructors with field initializers: If processedInitializers.InitializerStatements.IsDefault Then processedInitializers.InitializerStatements = processedInitializers.BoundInitializers.SelectAsArray(AddressOf RewriteInitializerAsStatement) Debug.Assert(processedInitializers.BoundInitializers.Length = processedInitializers.InitializerStatements.Length) End If Dim initializerStatements = processedInitializers.InitializerStatements Dim blockStatements As ImmutableArray(Of BoundStatement) = block.Statements Dim boundStatements = ArrayBuilder(Of BoundStatement).GetInstance() If constructorInitializerOpt IsNot Nothing Then ' Inserting base constructor call Debug.Assert(Not hasMyBaseConstructorCall) boundStatements.Add(constructorInitializerOpt) ElseIf hasMyBaseConstructorCall Then ' Using existing constructor call -- it must be the first statement in the block boundStatements.Add(blockStatements(0)) ElseIf Not constructorMethod.IsShared AndAlso containingType.IsValueType Then ' TODO: this can be skipped if we have equal number of initializers and fields ' assign all fields ' Me = Nothing Dim syntax = block.Syntax boundStatements.Add( New BoundExpressionStatement( syntax, New BoundAssignmentOperator( syntax, New BoundValueTypeMeReference(syntax, containingType), New BoundConversion( syntax, New BoundLiteral(syntax, ConstantValue.Null, Nothing), ConversionKind.WideningNothingLiteral, checked:=False, explicitCastInCode:=False, type:=containingType), suppressObjectClone:=True, type:=containingType))) End If ' add hookups for Handles if needed For Each member In containingType.GetMembers() If member.Kind = SymbolKind.Method Then Dim methodMember = DirectCast(member, MethodSymbol) Dim handledEvents = methodMember.HandledEvents If handledEvents.IsEmpty Then Continue For End If ' if method has definition and implementation parts ' their "Handles" should be merged. If methodMember.IsPartial Then Dim implementationPart = methodMember.PartialImplementationPart If implementationPart IsNot Nothing Then handledEvents = handledEvents.Concat(implementationPart.HandledEvents) Else ' partial methods with no implementation do not handle anything Continue For End If End If For Each handledEvent In handledEvents ' it should be either Constructor or SharedConstructor ' if it is an instance constructor it should apply to all instance constructors. If handledEvent.hookupMethod.MethodKind = constructorMethod.MethodKind Then Dim eventSymbol = DirectCast(handledEvent.EventSymbol, EventSymbol) Dim addHandlerMethod = eventSymbol.AddMethod Dim delegateCreation = handledEvent.delegateCreation Dim syntax = delegateCreation.Syntax Dim receiver As BoundExpression = Nothing If Not addHandlerMethod.IsShared Then Dim meParam = constructorMethod.MeParameter If TypeSymbol.Equals(addHandlerMethod.ContainingType, containingType, TypeCompareKind.ConsiderEverything) Then receiver = New BoundMeReference(syntax, meParam.Type).MakeCompilerGenerated() Else 'Dev10 always performs base call if event is in the base class. 'Even if Me/MyClass syntax was used. It seems to be somewhat of a bug 'that no-one cared about. For compat reasons we will do the same. receiver = New BoundMyBaseReference(syntax, meParam.Type).MakeCompilerGenerated() End If End If ' Normally, we would synthesize lowered bound nodes, but we know that these nodes will ' be run through the LocalRewriter. Let the LocalRewriter handle the special code for ' WinRT events. boundStatements.Add( New BoundAddHandlerStatement( syntax:=syntax, eventAccess:=New BoundEventAccess(syntax, receiver, eventSymbol, eventSymbol.Type).MakeCompilerGenerated(), handler:=delegateCreation).MakeCompilerGenerated()) End If Next End If Next ' insert initializers AFTER implicit or explicit call to a base constructor ' and after Handles hookup if there were any boundStatements.AddRange(initializerStatements) ' Add InitializeComponent call, if need to. If Not constructorMethod.IsShared AndAlso compilationState.InitializeComponentOpt IsNot Nothing AndAlso constructorMethod.IsImplicitlyDeclared Then boundStatements.Add(New BoundCall(constructorMethod.Syntax, compilationState.InitializeComponentOpt, Nothing, New BoundMeReference(constructorMethod.Syntax, compilationState.InitializeComponentOpt.ContainingType), ImmutableArray(Of BoundExpression).Empty, Nothing, compilationState.InitializeComponentOpt.ReturnType). MakeCompilerGenerated().ToStatement().MakeCompilerGenerated()) End If ' nothing was added If boundStatements.Count = 0 Then boundStatements.Free() Return block End If ' move the rest of the statements For statementIndex = If(hasMyBaseConstructorCall, 1, 0) To blockStatements.Length - 1 boundStatements.Add(blockStatements(statementIndex)) Next Return New BoundBlock(block.Syntax, block.StatementListSyntax, block.Locals, boundStatements.ToImmutableAndFree(), block.HasErrors) End Function Friend Function BuildScriptInitializerBody( initializerMethod As SynthesizedInteractiveInitializerMethod, processedInitializers As Binder.ProcessedFieldOrPropertyInitializers, block As BoundBlock) As BoundBlock Dim initializerStatements = RewriteInitializersAsStatements(initializerMethod, processedInitializers.BoundInitializers) processedInitializers.InitializerStatements = initializerStatements Dim boundStatements = ArrayBuilder(Of BoundStatement).GetInstance() boundStatements.AddRange(initializerStatements) boundStatements.AddRange(block.Statements) Return New BoundBlock(block.Syntax, block.StatementListSyntax, block.Locals, boundStatements.ToImmutableAndFree(), block.HasErrors) End Function ''' <summary> ''' Rewrites GlobalStatementInitializers to ExpressionStatements and gets the initializers for fields and properties. ''' </summary> ''' <remarks> ''' Initializers for fields and properties cannot be rewritten to their final form at this place because they might need ''' to be rewritten to replace their placeholder expressions to the final locals or temporaries (e.g. in case of a field ''' declaration with "AsNew" and multiple variable names. The final rewriting will during local rewriting. ''' The statement list returned by this function can be copied into the initializer without reprocessing it. ''' </remarks> Private Function RewriteInitializersAsStatements( method As SynthesizedInteractiveInitializerMethod, boundInitializers As ImmutableArray(Of BoundInitializer)) As ImmutableArray(Of BoundStatement) Dim boundStatements = ArrayBuilder(Of BoundStatement).GetInstance(boundInitializers.Length) Dim submissionResultType = method.ResultType Dim submissionResult As BoundExpression = Nothing For Each initializer In boundInitializers If submissionResultType IsNot Nothing AndAlso initializer Is boundInitializers.Last AndAlso initializer.Kind = BoundKind.GlobalStatementInitializer Then Dim statement = DirectCast(initializer, BoundGlobalStatementInitializer).Statement If statement.Kind = BoundKind.ExpressionStatement Then Dim expr = DirectCast(statement, BoundExpressionStatement).Expression Debug.Assert(expr.Type IsNot Nothing) If expr.Type.SpecialType <> SpecialType.System_Void Then submissionResult = expr Continue For End If End If End If boundStatements.Add(RewriteInitializerAsStatement(initializer)) Next If submissionResultType IsNot Nothing Then If submissionResult Is Nothing Then ' Return Nothing if submission does not have a trailing expression. submissionResult = New BoundLiteral(method.Syntax, ConstantValue.Nothing, submissionResultType) End If Debug.Assert(submissionResult.Type.SpecialType <> SpecialType.System_Void) ' The expression is converted to the submission result type when the initializer is bound. boundStatements.Add(New BoundReturnStatement(submissionResult.Syntax, submissionResult, method.FunctionLocal, method.ExitLabel)) End If Return boundStatements.ToImmutableAndFree() End Function Private Function RewriteInitializerAsStatement(initializer As BoundInitializer) As BoundStatement Select Case initializer.Kind Case BoundKind.FieldInitializer, BoundKind.PropertyInitializer Return initializer Case BoundKind.GlobalStatementInitializer Return DirectCast(initializer, BoundGlobalStatementInitializer).Statement Case Else Throw ExceptionUtilities.UnexpectedValue(initializer.Kind) End Select End Function ''' <summary> ''' Determines if this constructor calls another constructor of the constructor's containing class. ''' </summary> Friend Function HasExplicitMeConstructorCall(block As BoundBlock, container As TypeSymbol, <Out()> ByRef isMyBaseConstructorCall As Boolean) As Boolean isMyBaseConstructorCall = False If block.Statements.Any Then Dim firstBoundStatement As BoundStatement = block.Statements.First() ' NOTE: it is assumed that an explicit constructor call from another constructor should ' NOT be nested into any statement lists and to be the first constructor of the ' block's statements; otherwise it would complicate this rewriting because we ' will have to insert field initializers right after constructor call ' NOTE: If in future some rewriters break this assumption, the insertion ' of initializers as well as the following code should be revised If firstBoundStatement.Kind = BoundKind.ExpressionStatement Then Dim expression = DirectCast(firstBoundStatement, BoundExpressionStatement).Expression If expression.Kind = BoundKind.Call Then Dim callExpression = DirectCast(expression, BoundCall) Dim receiver = callExpression.ReceiverOpt If receiver IsNot Nothing AndAlso receiver.IsInstanceReference Then Dim methodSymbol = callExpression.Method If methodSymbol.MethodKind = MethodKind.Constructor Then isMyBaseConstructorCall = receiver.IsMyBaseReference Return TypeSymbol.Equals(methodSymbol.ContainingType, container, TypeCompareKind.ConsiderEverything) End If End If End If End If End If Return False End Function End Module End Namespace