LocalRewriter.cs 15.4 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7

using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
8
using Microsoft.CodeAnalysis.CSharp.Syntax;
P
Pilchie 已提交
9 10 11 12
using Microsoft.CodeAnalysis.RuntimeMembers;

namespace Microsoft.CodeAnalysis.CSharp
{
13
    internal sealed partial class LocalRewriter : BoundTreeRewriterWithStackGuard
P
Pilchie 已提交
14
    {
15 16 17 18 19 20
        private readonly CSharpCompilation _compilation;
        private readonly SyntheticBoundNodeFactory _factory;
        private readonly SynthesizedSubmissionFields _previousSubmissionFields;
        private readonly bool _allowOmissionOfConditionalCalls;
        private readonly LoweredDynamicOperationFactory _dynamicFactory;
        private bool _sawLambdas;
E
Evan Hauck 已提交
21
        private bool _sawLocalFunctions;
22 23 24 25 26
        private bool _inExpressionLambda;

        private bool _sawAwait;
        private bool _sawAwaitInExceptionHandler;
        private readonly DiagnosticBag _diagnostics;
27
        private readonly Instrumenter _instrumenter;
P
Pilchie 已提交
28

29 30 31
        private LocalRewriter(
            CSharpCompilation compilation,
            MethodSymbol containingMethod,
32
            int containingMethodOrdinal,
33 34 35
            NamedTypeSymbol containingType,
            SyntheticBoundNodeFactory factory,
            SynthesizedSubmissionFields previousSubmissionFields,
36
            bool allowOmissionOfConditionalCalls,
37 38
            DiagnosticBag diagnostics,
            Instrumenter instrumenter)
P
Pilchie 已提交
39
        {
40 41 42
            _compilation = compilation;
            _factory = factory;
            _factory.CurrentMethod = containingMethod;
43
            Debug.Assert(factory.CurrentType == (containingType ?? containingMethod.ContainingType));
44 45 46 47
            _dynamicFactory = new LoweredDynamicOperationFactory(factory, containingMethodOrdinal);
            _previousSubmissionFields = previousSubmissionFields;
            _allowOmissionOfConditionalCalls = allowOmissionOfConditionalCalls;
            _diagnostics = diagnostics;
48 49 50

            Debug.Assert(instrumenter != null);
            _instrumenter = instrumenter;
P
Pilchie 已提交
51 52 53 54 55 56
        }

        /// <summary>
        /// Lower a block of code by performing local rewritings.
        /// </summary>
        public static BoundStatement Rewrite(
57
            CSharpCompilation compilation,
58 59
            MethodSymbol method,
            int methodOrdinal,
P
Pilchie 已提交
60 61 62 63
            NamedTypeSymbol containingType,
            BoundStatement statement,
            TypeCompilationState compilationState,
            SynthesizedSubmissionFields previousSubmissionFields,
64
            bool allowOmissionOfConditionalCalls,
65
            DiagnosticBag diagnostics,
P
Pilchie 已提交
66
            out bool sawLambdas,
E
Evan Hauck 已提交
67
            out bool sawLocalFunctions,
P
Pilchie 已提交
68 69 70 71 72 73 74
            out bool sawAwaitInExceptionHandler)
        {
            Debug.Assert(statement != null);
            Debug.Assert(compilationState != null);

            try
            {
75
                var factory = new SyntheticBoundNodeFactory(method, statement.Syntax, compilationState, diagnostics);
76 77 78
                var localRewriter = new LocalRewriter(compilation, method, methodOrdinal, containingType, factory, previousSubmissionFields, allowOmissionOfConditionalCalls, diagnostics,
                                                     DebugInfoInjector.Singleton);

P
Pilchie 已提交
79
                var loweredStatement = (BoundStatement)localRewriter.Visit(statement);
80
                sawLambdas = localRewriter._sawLambdas;
E
Evan Hauck 已提交
81
                sawLocalFunctions = localRewriter._sawLocalFunctions;
82
                sawAwaitInExceptionHandler = localRewriter._sawAwaitInExceptionHandler;
P
Pilchie 已提交
83
                var block = loweredStatement as BoundBlock;
84
                var result = (block == null) ? loweredStatement : InsertPrologueSequencePoint(block, method);
P
Pilchie 已提交
85 86 87 88 89
                return result;
            }
            catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex)
            {
                diagnostics.Add(ex.Diagnostic);
E
Evan Hauck 已提交
90
                sawLambdas = sawLocalFunctions = sawAwaitInExceptionHandler = false;
P
Pilchie 已提交
91 92 93 94
                return new BoundBadStatement(statement.Syntax, ImmutableArray.Create<BoundNode>(statement), hasErrors: true);
            }
        }

95
        private bool Instrument
96 97 98
        {
            get
            {
99
                return !_inExpressionLambda;
100 101 102 103
            }
        }


P
Pilchie 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116 117
        // TODO(ngafter): This is a workaround.  Any piece of code that inserts a prologue
        // should be careful to insert any necessary sequence points too.
        private static BoundStatement InsertPrologueSequencePoint(BoundBlock body, MethodSymbol method)
        {
            // we need to insert a debug sequence point here for any prologue code
            // we'll associate it w/ the method declaration
            if (body != null && body.Statements.Length != 0 && !body.HasErrors)
            {
                var first = body.Statements.First();
                if (first.Kind != BoundKind.SequencePoint && first.Kind != BoundKind.SequencePointWithSpan)
                {
                    var asSourceMethod = method.ConstructedFrom as SourceMethodSymbol;
                    if ((object)asSourceMethod != null)
                    {
118
                        var syntax = asSourceMethod.BodySyntax as BlockSyntax;
P
Pilchie 已提交
119 120
                        if (syntax != null)
                        {
121
                            return AddSequencePoint(syntax, body);
P
Pilchie 已提交
122 123 124 125 126 127 128 129 130 131
                        }
                    }
                }
            }

            return body;
        }

        private PEModuleBuilder EmitModule
        {
132
            get { return _factory.CompilationState.ModuleBuilderOpt; }
P
Pilchie 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
        }

        public override BoundNode Visit(BoundNode node)
        {
            if (node == null)
            {
                return node;
            }
            Debug.Assert(!node.HasErrors, "nodes with errors should not be lowered");

            BoundExpression expr = node as BoundExpression;
            if (expr != null)
            {
                return VisitExpressionImpl(expr);
            }

            return node.Accept(this);
        }

        private BoundExpression VisitExpression(BoundExpression node)
        {
            if (node == null)
            {
                return node;
            }
            Debug.Assert(!node.HasErrors, "nodes with errors should not be lowered");

            return VisitExpressionImpl(node);
        }

        private BoundStatement VisitStatement(BoundStatement node)
        {
            if (node == null)
            {
                return node;
            }
            Debug.Assert(!node.HasErrors, "nodes with errors should not be lowered");

            return (BoundStatement)node.Accept(this);
        }

        private BoundExpression VisitExpressionImpl(BoundExpression node)
        {
            ConstantValue constantValue = node.ConstantValue;
            if (constantValue != null)
            {
                TypeSymbol type = node.Type;
180
                if (type?.IsNullableType() != true)
P
Pilchie 已提交
181 182 183 184 185
                {
                    return MakeLiteral(node.Syntax, constantValue, type);
                }
            }

186
            var visited = VisitExpressionWithStackGuard(node);
P
Pilchie 已提交
187 188 189 190 191 192 193 194 195 196 197 198

            // If you *really* need to change the type, consider using an indirect method
            // like compound assignment does (extra flag only passed when it is an expression
            // statement means that this constraint is not violated).
            // Dynamic type will be erased in emit phase. It is considered equivalent to Object in lowered bound trees.
            Debug.Assert(visited == null || visited.HasErrors || ReferenceEquals(visited.Type, node.Type) || visited.Type.Equals(node.Type, ignoreDynamic: true));

            return visited;
        }

        public override BoundNode VisitLambda(BoundLambda node)
        {
199 200
            _sawLambdas = true;
            var oldContainingSymbol = _factory.CurrentMethod;
P
Pilchie 已提交
201 202
            try
            {
203
                _factory.CurrentMethod = node.Symbol;
P
Pilchie 已提交
204 205 206 207
                return base.VisitLambda(node);
            }
            finally
            {
208
                _factory.CurrentMethod = oldContainingSymbol;
P
Pilchie 已提交
209 210 211
            }
        }

E
Evan Hauck 已提交
212 213 214 215 216 217
        public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
        {
            _sawLocalFunctions = true;
            var oldContainingSymbol = _factory.CurrentMethod;
            try
            {
218
                _factory.CurrentMethod = node.Symbol;
E
Evan Hauck 已提交
219 220 221 222 223 224 225 226
                return base.VisitLocalFunctionStatement(node);
            }
            finally
            {
                _factory.CurrentMethod = oldContainingSymbol;
            }
        }

P
Pilchie 已提交
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
        public override BoundNode VisitBadExpression(BoundBadExpression node)
        {
            // Cannot recurse into BadExpression children since the BadExpression
            // may represent being unable to use the child as an lvalue or rvalue.
            return node;
        }

        private static BoundStatement BadStatement(BoundNode node)
        {
            return (node == null)
                ? new BoundBadStatement(null, default(ImmutableArray<BoundNode>), true)
                : new BoundBadStatement(node.Syntax, ImmutableArray.Create<BoundNode>(node), true);
        }

        private static BoundExpression BadExpression(BoundExpression node)
        {
            return new BoundBadExpression(node.Syntax, LookupResultKind.NotReferencable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(node), node.Type);
        }

        private bool TryGetWellKnownTypeMember<TSymbol>(CSharpSyntaxNode syntax, WellKnownMember member, out TSymbol symbol, bool isOptional = false) where TSymbol : Symbol
        {
248
            symbol = (TSymbol)Binder.GetWellKnownTypeMember(_compilation, member, _diagnostics, syntax: syntax, isOptional: isOptional);
P
Pilchie 已提交
249 250 251 252 253 254
            return ((object)symbol != null);
        }

        private MethodSymbol GetSpecialTypeMethod(CSharpSyntaxNode syntax, SpecialMember specialMember)
        {
            MethodSymbol method;
255
            if (Binder.TryGetSpecialTypeMember(_compilation, specialMember, syntax, _diagnostics, out method))
P
Pilchie 已提交
256 257 258 259 260 261 262
            {
                return method;
            }
            else
            {
                MemberDescriptor descriptor = SpecialMembers.GetDescriptor(specialMember);
                SpecialType type = (SpecialType)descriptor.DeclaringTypeId;
263 264
                TypeSymbol container = _compilation.Assembly.GetSpecialType(type);
                TypeSymbol returnType = new ExtendedErrorTypeSymbol(compilation: _compilation, name: descriptor.Name, errorInfo: null, arity: descriptor.Arity);
P
Pilchie 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
                return new ErrorMethodSymbol(container, returnType, "Missing");
            }
        }

        public override BoundNode VisitTypeOfOperator(BoundTypeOfOperator node)
        {
            Debug.Assert((object)node.GetTypeFromHandle == null);

            var sourceType = (BoundTypeExpression)this.Visit(node.SourceType);
            var type = this.VisitType(node.Type);

            // Emit needs this helper
            MethodSymbol getTypeFromHandle;
            if (!TryGetWellKnownTypeMember(node.Syntax, WellKnownMember.System_Type__GetTypeFromHandle, out getTypeFromHandle))
            {
                return new BoundTypeOfOperator(node.Syntax, sourceType, null, type, hasErrors: true);
            }

            return node.Update(sourceType, getTypeFromHandle, type);
        }

        public override BoundNode VisitRefTypeOperator(BoundRefTypeOperator node)
        {
            Debug.Assert((object)node.GetTypeFromHandle == null);

            var operand = (BoundExpression)this.Visit(node.Operand);
            var type = this.VisitType(node.Type);

            // Emit needs this helper
            MethodSymbol getTypeFromHandle;
            if (!TryGetWellKnownTypeMember(node.Syntax, WellKnownMember.System_Type__GetTypeFromHandle, out getTypeFromHandle))
            {
                return new BoundRefTypeOperator(node.Syntax, operand, null, type, hasErrors: true);
            }

            return node.Update(operand, getTypeFromHandle, type);
        }
302 303 304 305 306 307

        public override BoundNode VisitTypeOrInstanceInitializers(BoundTypeOrInstanceInitializers node)
        {
            ImmutableArray<BoundStatement> rewrittenStatements = (ImmutableArray<BoundStatement>)this.VisitList(node.Statements);
            ImmutableArray<BoundStatement> optimizedStatements = ImmutableArray<BoundStatement>.Empty;

308
            if (_compilation.Options.OptimizationLevel == OptimizationLevel.Release)
309 310 311 312 313 314 315 316 317 318
            {
                // TODO: this part may conflict with InitializerRewriter.Rewrite in how it handles 
                //       the first field initializer (see 'if (i == 0)'...) which seems suspicious
                ArrayBuilder<BoundStatement> statements = ArrayBuilder<BoundStatement>.GetInstance();
                bool anyNonDefault = false;

                foreach (var initializer in rewrittenStatements)
                {
                    if (ShouldOptimizeOutInitializer(initializer))
                    {
319
                        if (_factory.CurrentMethod.IsStatic)
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
                        {
                            // NOTE: Dev11 removes static initializers if ONLY all of them are optimized out
                            statements.Add(initializer);
                        }
                    }
                    else
                    {
                        statements.Add(initializer);
                        anyNonDefault = true;
                    }
                }

                if (anyNonDefault)
                {
                    optimizedStatements = statements.ToImmutableAndFree();
                }
                else
                {
                    statements.Free();
                }
            }
            else
            {
                optimizedStatements = rewrittenStatements;
            }

            return new BoundStatementList(node.Syntax, optimizedStatements, node.HasErrors);
        }

        /// <summary>
        /// Returns true if the initializer is a field initializer which should be optimized out
        /// </summary>
        private static bool ShouldOptimizeOutInitializer(BoundStatement initializer)
        {
            BoundStatement statement = initializer;

            if (initializer.Kind == BoundKind.SequencePointWithSpan)
            {
                statement = ((BoundSequencePointWithSpan)initializer).StatementOpt;
            }
            else if (initializer.Kind == BoundKind.SequencePoint)
            {
                statement = ((BoundSequencePoint)initializer).StatementOpt;
            }

            if (statement == null || statement.Kind != BoundKind.ExpressionStatement)
            {
                return false;
            }

            BoundAssignmentOperator assignment = ((BoundExpressionStatement)statement).Expression as BoundAssignmentOperator;
            if (assignment == null)
            {
                return false;
            }

            Debug.Assert(assignment.Left.Kind == BoundKind.FieldAccess);

378 379 380 381 382 383
            var lhsField = ((BoundFieldAccess)assignment.Left).FieldSymbol;
            if (!lhsField.IsStatic && lhsField.ContainingType.IsStructType())
            {
                return false;
            }

384 385 386
            BoundExpression rhs = assignment.Right;
            return rhs.IsDefaultValue();
        }
P
Pilchie 已提交
387
    }
388
}