public override BoundNode VisitBlock(BoundBlock node) { var rewrittenLocals = node.Locals.WhereAsArray(local => local.IsCompilerGenerated || local.Name == null || this.GetVariable(local.Name) == null); var rewrittenLocalFunctions = node.LocalFunctions; var rewrittenStatements = VisitList(node.Statements); return node.Update(rewrittenLocals, rewrittenLocalFunctions, rewrittenStatements); }
internal static BoundNode Rewrite(CSharpCompilation compilation, EENamedTypeSymbol container, HashSet<LocalSymbol> declaredLocals, BoundNode node) { var builder = ArrayBuilder<BoundStatement>.GetInstance(); bool hasChanged; // Rewrite top-level declarations only. switch (node.Kind) { case BoundKind.LocalDeclaration: RewriteLocalDeclaration(compilation, container, declaredLocals, builder, (BoundLocalDeclaration)node); hasChanged = true; break; case BoundKind.MultipleLocalDeclarations: foreach (var declaration in ((BoundMultipleLocalDeclarations)node).LocalDeclarations) { RewriteLocalDeclaration(compilation, container, declaredLocals, builder, declaration); } hasChanged = true; break; default: hasChanged = false; break; } if (hasChanged) { node = new BoundBlock(node.Syntax, ImmutableArray<LocalSymbol>.Empty, builder.ToImmutable()) { WasCompilerGenerated = true }; } builder.Free(); return node; }
internal static BoundStatement AnalyzeMethodBody(MethodBodyCompiler methodCompiler, MethodSymbol method, BoundBlock body, DiagnosticBag diagnostics) { Debug.Assert(diagnostics != null); body = FlowAnalysisPass.Rewrite(method, body, diagnostics); var analyzedBody = (BoundBlock)RewritePass.Rewrite(methodCompiler, method.ContainingType, body); return RewritePass.InsertPrologueSequencePoint(analyzedBody, method); }
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) { var body = _generateMethodBody(this, diagnostics); var compilation = compilationState.Compilation; _lazyReturnType = CalculateReturnType(compilation, body); // Can't do this until the return type has been computed. TypeParameterChecker.Check(this, _allTypeParameters); if (diagnostics.HasAnyErrors()) { return; } DiagnosticsPass.IssueDiagnostics(compilation, body, diagnostics, this); if (diagnostics.HasAnyErrors()) { return; } // Check for use-site diagnostics (e.g. missing types in the signature). DiagnosticInfo useSiteDiagnosticInfo = null; this.CalculateUseSiteDiagnostic(ref useSiteDiagnosticInfo); if (useSiteDiagnosticInfo != null && useSiteDiagnosticInfo.Severity == DiagnosticSeverity.Error) { diagnostics.Add(useSiteDiagnosticInfo, this.Locations[0]); return; } var declaredLocals = PooledHashSet<LocalSymbol>.GetInstance(); try { // Rewrite local declaration statement. body = (BoundStatement)LocalDeclarationRewriter.Rewrite(compilation, _container, declaredLocals, body); // Verify local declaration names. foreach (var local in declaredLocals) { Debug.Assert(local.Locations.Length > 0); var name = local.Name; if (name.StartsWith("$", StringComparison.Ordinal)) { diagnostics.Add(ErrorCode.ERR_UnexpectedCharacter, local.Locations[0], name[0]); return; } } // Rewrite references to placeholder "locals". body = (BoundStatement)PlaceholderLocalRewriter.Rewrite(compilation, _container, declaredLocals, body); } finally { declaredLocals.Free(); } var syntax = body.Syntax; var statementsBuilder = ArrayBuilder<BoundStatement>.GetInstance(); statementsBuilder.Add(body); // Insert an implicit return statement if necessary. if (body.Kind != BoundKind.ReturnStatement) { statementsBuilder.Add(new BoundReturnStatement(syntax, expressionOpt: null)); } var localsBuilder = ArrayBuilder<LocalSymbol>.GetInstance(); var localsSet = PooledHashSet<LocalSymbol>.GetInstance(); foreach (var local in this.LocalsForBinding) { Debug.Assert(!localsSet.Contains(local)); localsBuilder.Add(local); localsSet.Add(local); } foreach (var local in this.Locals) { if (!localsSet.Contains(local)) { localsBuilder.Add(local); } } localsSet.Free(); body = new BoundBlock(syntax, localsBuilder.ToImmutableAndFree(), statementsBuilder.ToImmutableAndFree()) { WasCompilerGenerated = true }; Debug.Assert(!diagnostics.HasAnyErrors()); Debug.Assert(!body.HasErrors); bool sawLambdas; bool sawAwaitInExceptionHandler; body = LocalRewriter.Rewrite( compilation: this.DeclaringCompilation, method: this, methodOrdinal: _methodOrdinal, containingType: _container, statement: body, compilationState: compilationState, previousSubmissionFields: null, allowOmissionOfConditionalCalls: false, diagnostics: diagnostics, sawLambdas: out sawLambdas, sawAwaitInExceptionHandler: out sawAwaitInExceptionHandler); Debug.Assert(!sawAwaitInExceptionHandler); if (body.HasErrors) { return; } // Variables may have been captured by lambdas in the original method // or in the expression, and we need to preserve the existing values of // those variables in the expression. This requires rewriting the variables // in the expression based on the closure classes from both the original // method and the expression, and generating a preamble that copies // values into the expression closure classes. // // Consider the original method: // static void M() // { // int x, y, z; // ... // F(() => x + y); // } // and the expression in the EE: "F(() => x + z)". // // The expression is first rewritten using the closure class and local <1> // from the original method: F(() => <1>.x + z) // Then lambda rewriting introduces a new closure class that includes // the locals <1> and z, and a corresponding local <2>: F(() => <2>.<1>.x + <2>.z) // And a preamble is added to initialize the fields of <2>: // <2> = new <>c__DisplayClass0(); // <2>.<1> = <1>; // <2>.z = z; // Rewrite "this" and "base" references to parameter in this method. // Rewrite variables within body to reference existing display classes. body = (BoundStatement)CapturedVariableRewriter.Rewrite( this.SubstitutedSourceMethod.IsStatic ? null : _parameters[0], compilation.Conversions, _displayClassVariables, body, diagnostics); if (body.HasErrors) { Debug.Assert(false, "Please add a test case capturing whatever caused this assert."); return; } if (diagnostics.HasAnyErrors()) { return; } if (sawLambdas) { var closureDebugInfoBuilder = ArrayBuilder<ClosureDebugInfo>.GetInstance(); var lambdaDebugInfoBuilder = ArrayBuilder<LambdaDebugInfo>.GetInstance(); body = LambdaRewriter.Rewrite( loweredBody: body, thisType: this.SubstitutedSourceMethod.ContainingType, thisParameter: _thisParameter, method: this, methodOrdinal: _methodOrdinal, closureDebugInfoBuilder: closureDebugInfoBuilder, lambdaDebugInfoBuilder: lambdaDebugInfoBuilder, slotAllocatorOpt: null, compilationState: compilationState, diagnostics: diagnostics, assignLocals: true); // we don't need this information: closureDebugInfoBuilder.Free(); lambdaDebugInfoBuilder.Free(); } // Insert locals from the original method, // followed by any new locals. var block = (BoundBlock)body; var localBuilder = ArrayBuilder<LocalSymbol>.GetInstance(); foreach (var local in this.Locals) { Debug.Assert(!(local is EELocalSymbol) || (((EELocalSymbol)local).Ordinal == localBuilder.Count)); localBuilder.Add(local); } foreach (var local in block.Locals) { var oldLocal = local as EELocalSymbol; if (oldLocal != null) { Debug.Assert(localBuilder[oldLocal.Ordinal] == oldLocal); continue; } localBuilder.Add(local); } body = block.Update(localBuilder.ToImmutableAndFree(), block.Statements); TypeParameterChecker.Check(body, _allTypeParameters); compilationState.AddSynthesizedMethod(this, body); }
/// <summary> /// Lower a foreach loop that will enumerate a collection using an enumerator. /// /// E e = ((C)(x)).GetEnumerator() /// try { /// while (e.MoveNext()) { /// V v = (V)(T)e.Current; /// // body /// } /// } /// finally { /// // clean up e /// } /// </summary> private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; ForEachEnumeratorInfo enumeratorInfo = node.EnumeratorInfoOpt; Debug.Assert(enumeratorInfo != null); BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); TypeSymbol enumeratorType = enumeratorInfo.GetEnumeratorMethod.ReturnType; TypeSymbol elementType = enumeratorInfo.ElementType; // E e LocalSymbol enumeratorVar = new TempLocalSymbol(enumeratorType, RefKind.None, this.containingMethod); // Reference to e. BoundLocal boundEnumeratorVar = MakeBoundLocal(forEachSyntax, enumeratorVar, enumeratorType); // ((C)(x)).GetEnumerator() or (x).GetEnumerator(); BoundExpression enumeratorVarInitValue = SynthesizeCall(forEachSyntax, rewrittenExpression, enumeratorInfo.GetEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType); // E e = ((C)(x)).GetEnumerator(); BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue); AddForEachExpressionSequencePoint(forEachSyntax, ref enumeratorVarDecl); // V v LocalSymbol iterationVar = node.IterationVariable; //(V)(T)e.Current BoundExpression iterationVarAssignValue = SynthesizeConversion( syntax: forEachSyntax, operand: SynthesizeConversion( syntax: forEachSyntax, operand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.CurrentPropertyGetter), conversion: enumeratorInfo.CurrentConversion, type: elementType), conversion: node.ElementConversion, type: iterationVar.Type); // V v = (V)(T)e.Current; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarAssignValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* node.Body */ // } BoundStatement whileLoop = RewriteWhileStatement( syntax: forEachSyntax, rewrittenCondition: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, method: enumeratorInfo.MoveNextMethod), conditionSequencePointSpan: forEachSyntax.InKeyword.Span, rewrittenBody: new BoundBlock(rewrittenBody.Syntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVarDecl, rewrittenBody), localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar)), breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: false); BoundStatement result; if (enumeratorInfo.DisposeMethodOpt != null) { BoundBlock finallyBlockOpt; var idisposableTypeSymbol = enumeratorInfo.DisposeMethodOpt.ContainingType; var conversions = new TypeConversions(this.containingMethod.ContainingAssembly.CorLibrary); if (conversions.ClassifyImplicitConversion(enumeratorType, idisposableTypeSymbol).IsImplicit) { Debug.Assert(enumeratorInfo.DisposeMethodOpt != null); Conversion receiverConversion = enumeratorType.IsStructType() ? Conversion.Boxing : Conversion.ImplicitReference; // ((IDisposable)e).Dispose(); or e.Dispose(); BoundStatement disposeCall = new BoundExpressionStatement(forEachSyntax, expression: SynthesizeCall(forEachSyntax, boundEnumeratorVar, enumeratorInfo.DisposeMethodOpt, receiverConversion, idisposableTypeSymbol)); BoundStatement disposeStmt; if (enumeratorType.IsValueType) { // No way for the struct to be nullable and disposable. Debug.Assert(((TypeSymbol)enumeratorType.OriginalDefinition).SpecialType != SpecialType.System_Nullable_T); // For non-nullable structs, no null check is required. disposeStmt = disposeCall; } else { // NB: cast to object missing from spec. Needed to ignore user-defined operators and box type parameters. // if ((object)e != null) ((IDisposable)e).Dispose(); disposeStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, left: SynthesizeConversion( syntax: forEachSyntax, operand: boundEnumeratorVar, conversion: enumeratorInfo.EnumeratorConversion, type: this.compilation.GetSpecialType(SpecialType.System_Object)), right: new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: this.compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: disposeCall, rewrittenAlternativeOpt: null, hasErrors: false); } finallyBlockOpt = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.Null, statements: ReadOnlyArray<BoundStatement>.CreateFrom(disposeStmt)); } else { Debug.Assert(!enumeratorType.IsSealed); // IDisposable d LocalSymbol disposableVar = new TempLocalSymbol(idisposableTypeSymbol, RefKind.None, this.containingMethod); // Reference to d. BoundLocal boundDisposableVar = MakeBoundLocal(forEachSyntax, disposableVar, idisposableTypeSymbol); BoundTypeExpression boundIDisposableTypeExpr = new BoundTypeExpression(forEachSyntax, type: idisposableTypeSymbol); // e as IDisposable BoundExpression disposableVarInitValue = new BoundAsOperator(forEachSyntax, operand: boundEnumeratorVar, targetType: boundIDisposableTypeExpr, conversion: Conversion.ExplicitReference, // Explicit so the emitter won't optimize it away. type: idisposableTypeSymbol); // IDisposable d = e as IDisposable; BoundStatement disposableVarDecl = MakeLocalDeclaration(forEachSyntax, disposableVar, disposableVarInitValue); // if (d != null) d.Dispose(); BoundStatement ifStmt = RewriteIfStatement( syntax: forEachSyntax, rewrittenCondition: new BoundBinaryOperator(forEachSyntax, operatorKind: BinaryOperatorKind.NotEqual, // reference equality left: boundDisposableVar, right: new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Null, type: null), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: this.compilation.GetSpecialType(SpecialType.System_Boolean)), rewrittenConsequence: new BoundExpressionStatement(forEachSyntax, expression: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundDisposableVar, method: enumeratorInfo.DisposeMethodOpt)), rewrittenAlternativeOpt: null, hasErrors: false); // IDisposable d = e as IDisposable; // if (d != null) d.Dispose(); finallyBlockOpt = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(disposableVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(disposableVarDecl, ifStmt)); } // try { // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } // } // finally { // /* dispose of e */ // } BoundStatement tryFinally = new BoundTryStatement(forEachSyntax, tryBlock: new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.Empty, statements: ReadOnlyArray<BoundStatement>.CreateFrom(whileLoop)), catchBlocks: ReadOnlyArray<BoundCatchBlock>.Empty, finallyBlockOpt: finallyBlockOpt); // E e = ((C)(x)).GetEnumerator(); // try { // /* as above */ result = new BoundBlock( syntax: forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(enumeratorVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(enumeratorVarDecl, tryFinally)); } else { // E e = ((C)(x)).GetEnumerator(); // while (e.MoveNext()) { // V v = (V)(T)e.Current; // /* loop body */ // } result = new BoundBlock( syntax: forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(enumeratorVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(enumeratorVarDecl, whileLoop)); } AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = new TempLocalSymbol(intType, RefKind.None, containingMethod); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray<BoundExpression>.CreateFrom((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVarDecl, rewrittenBody)); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetUpperBound); // work from most-nested to least-nested // for (A[...] a = /*node.Expression*/; int p_0 = a.GetLowerBound(0); p_0 <= a.GetUpperBound(0); p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= a.GetUpperBound(0); p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ReadOnlyArray<BoundExpression> dimensionArgument = ReadOnlyArray<BoundExpression>.CreateFrom( new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // a.GetUpperBound(/*dimension*/) //CONSIDER: dev10 creates a temp for each dimension's upper bound BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); ReadOnlyArray<LocalSymbol> locals; BoundStatement initializer; GeneratedLabelSymbol breakLabel; if (dimension == 0) { // outermost for-loop locals = ReadOnlyArray<LocalSymbol>.CreateFrom(arrayVar, positionVar[dimension]); initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(arrayVarDecl, positionVarDecl)); breakLabel = node.BreakLabel; // i.e. the one that break statements will jump to } else { locals = ReadOnlyArray<LocalSymbol>.CreateFrom(positionVar[dimension]); initializer = positionVarDecl; breakLabel = new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused } // p_/*dimension*/ <= a.GetUpperBound(/*dimension*/) //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: currentDimensionUpperBound, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if(forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( node.Syntax, locals, initializer, exitCondition, forEachSyntax.InKeyword, positionIncrement, body, breakLabel, continueLabel, node.HasErrors); } Debug.Assert(forLoop != null); AddForEachExpressionSequencePoint(forEachSyntax, ref forLoop); return forLoop; }
private void EmitStatements(BoundBlock node) { IL.MarkSequencePoint(node.Location); foreach (var statement in node.Nodes) { EmitStatement(statement); } }
public override BoundNode VisitBlock(BoundBlock node) { Debug.Assert(EvalStackIsEmpty(), "entering blocks when evaluation stack is not empty?"); // normally we would not allow stack locals // when evaluation stack is not empty. DeclareLocals(node.Locals, 0); return base.VisitBlock(node); }
// used by HandleReturn method which tries to inject // indirect ret sequence as a last statement in the block // that is the last statement of the current method // NOTE: it is important that there is no code after this "ret" // it is desirable, for debug purposes, that this ret is emitted inside top level { } private bool IsLastBlockInMethod(BoundBlock block) { if (_boundBody == block) { return true; } //sometimes top level node is a statement list containing //epilogue and then a block. If we are having that block, it will do. var list = _boundBody as BoundStatementList; if (list != null && list.Statements.LastOrDefault() == block) { return true; } return false; }
public virtual void VisitBlock(BoundBlock node) { DefaultVisit(node); }
public override BoundNode VisitBlock(BoundBlock node) { // First check whether we have work. bool haveWork = false; foreach (var temporary in node.Temporaries) { if (_statistics[temporary].ShouldReplace) { haveWork = true; break; } } // If we don't have work, just cascade. if (!haveWork) return base.VisitBlock(node); // Create a new list of temporaries with the variables to be // squelched removed. var newTemporaries = new ReadOnlyArray<BoundTemporary>.Builder(); foreach (var temporary in node.Temporaries) { if (_statistics[temporary].ShouldReplace) temporary.Type.MarkUnused(); else newTemporaries.Add(temporary); } var temporaries = newTemporaries.ToReadOnly(); // Rebuild the nodes with the new rules applied. var nodes = new ReadOnlyArray<BoundStatement>.Builder(); foreach (var statement in node.Nodes) { var setVariable = statement as BoundSetVariable; if (setVariable != null) { setVariable = (BoundSetVariable)Visit(setVariable); // If the set variable reduced to an assignment to itself, // remove the set variable. This happens when the variable // of the set variable is replaced. var getVariable = setVariable.Value as BoundGetVariable; if (getVariable != null && setVariable.Variable == getVariable.Variable) continue; // If we're going to squelch this local, remove the // set variable for it. var temporary = setVariable.Variable as BoundTemporary; if (temporary != null && _statistics[temporary].ShouldRemove) continue; } nodes.Add((BoundStatement)Visit(statement)); } // Return the new block. return new BoundBlock(temporaries, nodes.ToReadOnly(), node.Location); }
// block introduces a new scope public override BoundNode VisitBlock(BoundBlock node) { var rewrittenStatements = VisitList(node.Statements); return new BoundBlock(node.Syntax, node.LocalsOpt, rewrittenStatements, node.HasErrors); }
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) { ImmutableArray <LocalSymbol> declaredLocalsArray; var body = _generateMethodBody(this, diagnostics, out declaredLocalsArray, out _lazyResultProperties); var compilation = compilationState.Compilation; _lazyReturnType = CalculateReturnType(compilation, body); // Can't do this until the return type has been computed. TypeParameterChecker.Check(this, _allTypeParameters); if (diagnostics.HasAnyErrors()) { return; } DiagnosticsPass.IssueDiagnostics(compilation, body, diagnostics, this); if (diagnostics.HasAnyErrors()) { return; } // Check for use-site diagnostics (e.g. missing types in the signature). DiagnosticInfo useSiteDiagnosticInfo = null; this.CalculateUseSiteDiagnostic(ref useSiteDiagnosticInfo); if (useSiteDiagnosticInfo != null && useSiteDiagnosticInfo.Severity == DiagnosticSeverity.Error) { diagnostics.Add(useSiteDiagnosticInfo, this.Locations[0]); return; } try { var declaredLocals = PooledHashSet <LocalSymbol> .GetInstance(); try { // Rewrite local declaration statement. body = (BoundStatement)LocalDeclarationRewriter.Rewrite( compilation, _container, declaredLocals, body, declaredLocalsArray, diagnostics); // Verify local declaration names. foreach (var local in declaredLocals) { Debug.Assert(local.Locations.Length > 0); var name = local.Name; if (name.StartsWith("$", StringComparison.Ordinal)) { diagnostics.Add(ErrorCode.ERR_UnexpectedCharacter, local.Locations[0], name[0]); return; } } // Rewrite references to placeholder "locals". body = (BoundStatement)PlaceholderLocalRewriter.Rewrite(compilation, _container, declaredLocals, body, diagnostics); if (diagnostics.HasAnyErrors()) { return; } } finally { declaredLocals.Free(); } var syntax = body.Syntax; var statementsBuilder = ArrayBuilder <BoundStatement> .GetInstance(); statementsBuilder.Add(body); // Insert an implicit return statement if necessary. if (body.Kind != BoundKind.ReturnStatement) { statementsBuilder.Add(new BoundReturnStatement(syntax, RefKind.None, expressionOpt: null)); } var localsBuilder = ArrayBuilder <LocalSymbol> .GetInstance(); var localsSet = PooledHashSet <LocalSymbol> .GetInstance(); foreach (var local in this.LocalsForBinding) { Debug.Assert(!localsSet.Contains(local)); localsBuilder.Add(local); localsSet.Add(local); } foreach (var local in this.Locals) { if (!localsSet.Contains(local)) { Debug.Assert(!localsSet.Contains(local)); localsBuilder.Add(local); localsSet.Add(local); } } localsSet.Free(); body = new BoundBlock(syntax, localsBuilder.ToImmutableAndFree(), statementsBuilder.ToImmutableAndFree()) { WasCompilerGenerated = true }; Debug.Assert(!diagnostics.HasAnyErrors()); Debug.Assert(!body.HasErrors); bool sawLambdas; bool sawLocalFunctions; bool sawAwaitInExceptionHandler; ImmutableArray <SourceSpan> dynamicAnalysisSpans = ImmutableArray <SourceSpan> .Empty; body = LocalRewriter.Rewrite( compilation: this.DeclaringCompilation, method: this, methodOrdinal: _methodOrdinal, containingType: _container, statement: body, compilationState: compilationState, previousSubmissionFields: null, allowOmissionOfConditionalCalls: false, instrumentForDynamicAnalysis: false, debugDocumentProvider: null, dynamicAnalysisSpans: ref dynamicAnalysisSpans, diagnostics: diagnostics, sawLambdas: out sawLambdas, sawLocalFunctions: out sawLocalFunctions, sawAwaitInExceptionHandler: out sawAwaitInExceptionHandler); Debug.Assert(!sawAwaitInExceptionHandler); Debug.Assert(dynamicAnalysisSpans.Length == 0); if (body.HasErrors) { return; } // Variables may have been captured by lambdas in the original method // or in the expression, and we need to preserve the existing values of // those variables in the expression. This requires rewriting the variables // in the expression based on the closure classes from both the original // method and the expression, and generating a preamble that copies // values into the expression closure classes. // // Consider the original method: // static void M() // { // int x, y, z; // ... // F(() => x + y); // } // and the expression in the EE: "F(() => x + z)". // // The expression is first rewritten using the closure class and local <1> // from the original method: F(() => <1>.x + z) // Then lambda rewriting introduces a new closure class that includes // the locals <1> and z, and a corresponding local <2>: F(() => <2>.<1>.x + <2>.z) // And a preamble is added to initialize the fields of <2>: // <2> = new <>c__DisplayClass0(); // <2>.<1> = <1>; // <2>.z = z; // Rewrite "this" and "base" references to parameter in this method. // Rewrite variables within body to reference existing display classes. body = (BoundStatement)CapturedVariableRewriter.Rewrite( this.SubstitutedSourceMethod.IsStatic ? null : _parameters[0], compilation.Conversions, _displayClassVariables, body, diagnostics); if (body.HasErrors) { Debug.Assert(false, "Please add a test case capturing whatever caused this assert."); return; } if (diagnostics.HasAnyErrors()) { return; } if (sawLambdas || sawLocalFunctions) { var closureDebugInfoBuilder = ArrayBuilder <ClosureDebugInfo> .GetInstance(); var lambdaDebugInfoBuilder = ArrayBuilder <LambdaDebugInfo> .GetInstance(); body = LambdaRewriter.Rewrite( loweredBody: body, thisType: this.SubstitutedSourceMethod.ContainingType, thisParameter: _thisParameter, method: this, methodOrdinal: _methodOrdinal, substitutedSourceMethod: this.SubstitutedSourceMethod.OriginalDefinition, closureDebugInfoBuilder: closureDebugInfoBuilder, lambdaDebugInfoBuilder: lambdaDebugInfoBuilder, slotAllocatorOpt: null, compilationState: compilationState, diagnostics: diagnostics, assignLocals: true); // we don't need this information: closureDebugInfoBuilder.Free(); lambdaDebugInfoBuilder.Free(); } // Insert locals from the original method, // followed by any new locals. var block = (BoundBlock)body; var localBuilder = ArrayBuilder <LocalSymbol> .GetInstance(); foreach (var local in this.Locals) { Debug.Assert(!(local is EELocalSymbol) || (((EELocalSymbol)local).Ordinal == localBuilder.Count)); localBuilder.Add(local); } foreach (var local in block.Locals) { var oldLocal = local as EELocalSymbol; if (oldLocal != null) { Debug.Assert(localBuilder[oldLocal.Ordinal] == oldLocal); continue; } localBuilder.Add(local); } body = block.Update(localBuilder.ToImmutableAndFree(), block.LocalFunctions, block.Statements); TypeParameterChecker.Check(body, _allTypeParameters); compilationState.AddSynthesizedMethod(this, body); } catch (BoundTreeVisitor.CancelledByStackGuardException ex) { ex.AddAnError(diagnostics); } }
public override BoundNode VisitBlock(BoundBlock node) { AddVariables(node.Locals); return(base.VisitBlock(node)); }
public override void VisitCFGBlock(BoundBlock x) { VisitCFGBlockInit(x); VisitCFGBlockInternal(x); // modifies _state, traverses to the edge }
protected override void EndBlock(BoundBlock block) { var blockSyntax = block.Syntax as BlockSyntax; if (blockSyntax == null) { return; } NoteLocation(blockSyntax.CloseBraceToken.Span); }
public override BoundNode VisitBlock(BoundBlock node) { var newLocals = RewriteLocals(node.Locals); var newLocalFunctions = node.LocalFunctions; var newStatements = VisitList(node.Statements); return node.Update(newLocals, newLocalFunctions, newStatements); }
/// <summary> /// Lower a foreach loop that will enumerate the characters of a string. /// /// string s = x; /// for (int p = 0; p < s.Length; p = p + 1) { /// V v = (V)s.Chars[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's /// implementation of IEnumerable and just indexing into its characters. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); TypeSymbol stringType = collectionExpression.Type; Debug.Assert(stringType.SpecialType == SpecialType.System_String); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // string s; LocalSymbol stringVar = new TempLocalSymbol(stringType, RefKind.None, containingMethod); // int p; LocalSymbol positionVar = new TempLocalSymbol(intType, RefKind.None, containingMethod); // Reference to s. BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // string s = /*expr*/; BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl); // int p = 0; BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar, new BoundLiteral(forEachSyntax, ConstantValue.ConstantValueZero.Int32, intType)); // string s = /*node.Expression*/; int p = 0; BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(stringVarDecl, positionVariableDecl)); BoundExpression stringLength = BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_String__Length), arguments: ReadOnlyArray<BoundExpression>.Empty); // p < s.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: stringLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; Debug.Assert(node.ElementConversion.Exists); // (V)s.Chars[p] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__Chars), arguments: ReadOnlyArray<BoundExpression>.CreateFrom(boundPositionVar)), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)s.Chars[p]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)s.Chars[p]; /*node.Body*/ } BoundStatement loopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVarDecl, rewrittenBody)); // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) { // V v = (V)s.Chars[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: forEachSyntax, locals: ReadOnlyArray<LocalSymbol>.CreateFrom(stringVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntax: forEachSyntax.InKeyword, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
private void EmitBlock(BoundBlock block) { var hasLocals = !block.Locals.IsEmpty; if (hasLocals) { _builder.OpenLocalScope(); foreach (var local in block.Locals) { var declaringReferences = local.DeclaringSyntaxReferences; DefineLocal(local, !declaringReferences.IsEmpty ? (CSharpSyntaxNode)declaringReferences[0].GetSyntax() : block.Syntax); } } foreach (var statement in block.Statements) { EmitStatement(statement); } if (_indirectReturnState == IndirectReturnState.Needed && IsLastBlockInMethod(block)) { HandleReturn(); } if (hasLocals) { foreach (var local in block.Locals) { FreeLocal(local); } _builder.CloseLocalScope(); } }
/// <summary> /// Lower a foreach loop that will enumerate a single-dimensional array. /// /// A[] a = x; /// for (int p = 0; p < a.Length; p = p + 1) { /// V v = (V)a[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; Debug.Assert(arrayType.Rank == 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p LocalSymbol positionVar = new TempLocalSymbol(intType, RefKind.None, containingMethod); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // int p = 0; BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar, new BoundLiteral(forEachSyntax, ConstantValue.ConstantValueZero.Int32, intType)); // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess( syntax: forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray<BoundExpression>.CreateFrom(boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p]; BoundStatement iterationVariableDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVariableDecl); BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(arrayVarDecl, positionVarDecl)); // a.Length BoundExpression arrayLength = new BoundArrayLength( syntax: forEachSyntax, expression: boundArrayVar, type: intType); // p < a.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: arrayLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); // { V v = (V)a[p]; /* node.Body */ } BoundStatement loopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVariableDecl, rewrittenBody)); // for (A[] a = /*node.Expression*/, int p = 0; p < a.Length; p = p + 1) { // V v = (V)a[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: node.Syntax, locals: ReadOnlyArray<LocalSymbol>.CreateFrom(arrayVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntax: forEachSyntax.InKeyword, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
public static void Process(BoundBlock block, Binder binder, ArrayBuilder<string> builder) { var visitor = new NonMoveableVariableVisitor(binder, builder); visitor.Visit(block); }
internal ControlFlowGraphVertex(AnalysisEngine engine, SourceRoutineSymbol routine, BoundBlock block) { this.Engine = engine; this.routine = routine; this.block = block; this.File = routine.ContainingFile.SyntaxTree.FilePath; this.Vid = this.block.Ordinal + this.File.GetHashCode(); this.BlockName = this.block.DebugDisplay; this.Kind = this.block.Kind.ToString(); this.RoutineName = this.routine.Name; this.Statements = this.block.Statements != null ? block.Statements.Count : 0; LangElement l = PhylDiagnosingVisitor.PickFirstSyntaxNode(this.block); if (l != null) { Tuple <int, int> pos = engine.GetLineFromTokenPosition(l.Span.Start, this.File); this.Line = pos.Item1; this.Column = pos.Item2; } }