| File: Lowering\InitializerRewriter.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// 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. #nullable disable using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { internal static class InitializerRewriter { internal static BoundTypeOrInstanceInitializers RewriteConstructor(ImmutableArray<BoundInitializer> boundInitializers, MethodSymbol method) { Debug.Assert(!boundInitializers.IsDefault); Debug.Assert((method.MethodKind == MethodKind.Constructor) || (method.MethodKind == MethodKind.StaticConstructor)); var sourceMethod = method as SourceMemberMethodSymbol; var syntax = ((object)sourceMethod != null) ? sourceMethod.SyntaxNode : method.GetNonNullSyntaxNode(); return new BoundTypeOrInstanceInitializers(syntax, boundInitializers.SelectAsArray(RewriteInitializersAsStatements)); } internal static BoundTypeOrInstanceInitializers RewriteScriptInitializer(ImmutableArray<BoundInitializer> boundInitializers, SynthesizedInteractiveInitializerMethod method, out bool hasTrailingExpression) { Debug.Assert(!boundInitializers.IsDefault); var boundStatements = ArrayBuilder<BoundStatement>.GetInstance(boundInitializers.Length); var submissionResultType = method.ResultType; var hasSubmissionResultType = (object)submissionResultType != null; BoundStatement lastStatement = null; BoundExpression trailingExpression = null; foreach (var initializer in boundInitializers) { // The value of the last expression statement (if any) is returned from the submission initializer, // unless this is a #load'ed tree. I the #load'ed tree case, we'll execute the trailing expression // but discard its result. if (hasSubmissionResultType && (initializer == boundInitializers.Last()) && (initializer.Kind == BoundKind.GlobalStatementInitializer) && method.DeclaringCompilation.IsSubmissionSyntaxTree(initializer.SyntaxTree)) { lastStatement = ((BoundGlobalStatementInitializer)initializer).Statement; var expression = GetTrailingScriptExpression(lastStatement); if (expression != null && (object)expression.Type != null && !expression.Type.IsVoidType()) { trailingExpression = expression; continue; } } boundStatements.Add(RewriteInitializersAsStatements(initializer)); } if (hasSubmissionResultType && (trailingExpression != null)) { Debug.Assert(!submissionResultType.IsVoidType()); // Note: The trailing expression was already converted to the submission result type in Binder.BindGlobalStatement. boundStatements.Add(new BoundReturnStatement(lastStatement.Syntax, RefKind.None, trailingExpression, @checked: false)); hasTrailingExpression = true; } else { hasTrailingExpression = false; } return new BoundTypeOrInstanceInitializers(method.GetNonNullSyntaxNode(), boundStatements.ToImmutableAndFree()); } /// <summary> /// Returns the expression if the statement is actually an expression (ExpressionStatementSyntax with no trailing semicolon). /// </summary> internal static BoundExpression GetTrailingScriptExpression(BoundStatement statement) { return (statement.Kind == BoundKind.ExpressionStatement) && ((ExpressionStatementSyntax)statement.Syntax).SemicolonToken.IsMissing ? ((BoundExpressionStatement)statement).Expression : null; } private static BoundStatement RewriteFieldInitializer(BoundFieldEqualsValue fieldInit) { var field = fieldInit.Field; SyntaxNode syntax = fieldInit.Syntax; syntax = (syntax as EqualsValueClauseSyntax)?.Value ?? syntax; //we want the attached sequence point to indicate the value node var boundReceiver = field.IsStatic ? null : new BoundThisReference(syntax, field.ContainingType); BoundStatement boundStatement = new BoundExpressionStatement(syntax, new BoundAssignmentOperator(syntax, new BoundFieldAccess(syntax, boundReceiver, field, constantValueOpt: null), fieldInit.Value, field.Type, isRef: field.RefKind != RefKind.None) { WasCompilerGenerated = true }) { WasCompilerGenerated = !fieldInit.Locals.IsEmpty || fieldInit.WasCompilerGenerated }; if (!fieldInit.Locals.IsEmpty) { boundStatement = new BoundBlock(syntax, fieldInit.Locals, ImmutableArray.Create(boundStatement)) { WasCompilerGenerated = fieldInit.WasCompilerGenerated }; } Debug.Assert(LocalRewriter.IsFieldOrPropertyInitializer(boundStatement)); return boundStatement; } private static BoundStatement RewriteInitializersAsStatements(BoundInitializer initializer) { switch (initializer.Kind) { case BoundKind.FieldEqualsValue: return RewriteFieldInitializer((BoundFieldEqualsValue)initializer); case BoundKind.GlobalStatementInitializer: return ((BoundGlobalStatementInitializer)initializer).Statement; default: throw ExceptionUtilities.UnexpectedValue(initializer.Kind); } } } }