internal static BoundBlock Rewrite(SourceMethodSymbol sourceMethodSymbol, MethodContractSyntax contract, BoundBlock body, TypeCompilationState compilationState, DiagnosticBag diagsForCurrentMethod) { var binder = compilationState.Compilation.GetBinderFactory(sourceMethodSymbol.SyntaxTree) .GetBinder(body.Syntax); SyntheticBoundNodeFactory factory = new SyntheticBoundNodeFactory(sourceMethodSymbol, sourceMethodSymbol.SyntaxNode, compilationState, diagsForCurrentMethod); var contractType = compilationState.Compilation.GetTypeByReflectionType(typeof(System.Diagnostics.Contracts.Contract), diagsForCurrentMethod); var contractStatements = ArrayBuilder<BoundStatement>.GetInstance(contract.Requires.Count); foreach (var requires in contract.Requires) { var condition = binder.BindExpression(requires.Condition, diagsForCurrentMethod); var methodCall = factory.StaticCall(contractType, "Requires", condition); var statement = factory.ExpressionStatement(methodCall); contractStatements.Add(statement); } foreach (var requires in contract.Ensures) { var condition = binder.BindExpression(requires.Condition, diagsForCurrentMethod); var methodCall = factory.StaticCall(contractType, "Ensures", condition); var statement = factory.ExpressionStatement(methodCall); contractStatements.Add(statement); } return body.Update(body.Locals, body.Statements.InsertRange(0, contractStatements.ToImmutableAndFree())); }
private BoundExpression VisitExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression) { // wrap the node in a spill sequence to mark the fact that it must be moved up the tree. // The caller will handle this node type if the result is discarded. if (expression != null && expression.Kind == BoundKind.AwaitExpression) { // we force the await expression to be assigned to a temp variable var awaitExpression = (BoundAwaitExpression)expression; awaitExpression = awaitExpression.Update( VisitExpression(ref builder, awaitExpression.Expression), awaitExpression.GetAwaiter, awaitExpression.IsCompleted, awaitExpression.GetResult, awaitExpression.Type); var syntax = awaitExpression.Syntax; Debug.Assert(syntax.IsKind(SyntaxKind.AwaitExpression)); _F.Syntax = syntax; BoundAssignmentOperator assignToTemp; var replacement = _F.StoreToTemp(awaitExpression, out assignToTemp, kind: SynthesizedLocalKind.AwaitSpill, syntaxOpt: syntax); if (builder == null) { builder = new BoundSpillSequenceBuilder(); } builder.AddLocal(replacement.LocalSymbol, _F.Diagnostics); builder.AddStatement(_F.ExpressionStatement(assignToTemp)); return(replacement); } var e = (BoundExpression)this.Visit(expression); if (e == null || e.Kind != SpillSequenceBuilder) { return(e); } var newBuilder = (BoundSpillSequenceBuilder)e; if (builder == null) { builder = newBuilder.Update(null); } else { builder.Include(newBuilder); } return(newBuilder.Value); }
internal void AddSequence(SyntheticBoundNodeFactory F, BoundSequence sequence) { locals.AddRange(sequence.Locals); foreach (var sideEffect in sequence.SideEffects) { statements.Add(F.ExpressionStatement(sideEffect)); } }
internal void AddSequence(SyntheticBoundNodeFactory F, BoundSequence sequence) { locals.AddRange(sequence.Locals); foreach (var sideEffect in sequence.SideEffects) { statements.Add(F.ExpressionStatement(sideEffect)); } }
private BoundExpression VisitExpression(ref BoundSpillSequence2 ss, BoundExpression expression) { // wrap the node in a spill sequence to mark the fact that it must be moved up the tree. // The caller will handle this node type if the result is discarded. if (expression != null && expression.Kind == BoundKind.AwaitExpression) { // we force the await expression to be assigned to a temp variable var awaitExpression = (BoundAwaitExpression)expression; awaitExpression = awaitExpression.Update( VisitExpression(ref ss, awaitExpression.Expression), awaitExpression.GetAwaiter, awaitExpression.IsCompleted, awaitExpression.GetResult, awaitExpression.Type); BoundAssignmentOperator assignToTemp; var replacement = F.StoreToTemp(awaitExpression, out assignToTemp, kind: SynthesizedLocalKind.AwaitSpilledTemp); if (ss == null) { ss = new BoundSpillSequence2(); } ss.Add(replacement.LocalSymbol); writeOnceTemps.Add(replacement.LocalSymbol); F.Syntax = awaitExpression.Syntax; ss.Add(F.ExpressionStatement(assignToTemp)); return(replacement); } var e = VisitExpression(expression); if (e == null || e.Kind != SpillSequence2) { return(e); } var newss = (BoundSpillSequence2)e; if (ss == null) { ss = newss.Update(null); } else { ss.IncludeSequence(newss); } return(newss.Value); }
public override BoundNode VisitSequence(BoundSequence node) { ReadOnlyArray <BoundExpression> sideEffects = (ReadOnlyArray <BoundExpression>) this.VisitList(node.SideEffects); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence) { return(node.Update(node.Locals, sideEffects, value, type)); } var spillBuilder = new SpillBuilder(); spillBuilder.Locals.AddRange(node.Locals); foreach (var sideEffect in sideEffects) { spillBuilder.Statements.Add( (sideEffect.Kind == BoundKind.SpillSequence) ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect) : F.ExpressionStatement(sideEffect)); } BoundExpression newValue; if (value.Kind == BoundKind.SpillSequence) { var awaitEffect = (BoundSpillSequence)value; spillBuilder.AddSpill(awaitEffect); newValue = awaitEffect.Value; } else { newValue = value; } return(spillBuilder.BuildSequenceAndFree(F, newValue)); }
private void AddBindings(ArrayBuilder <BoundStatement> sectionBuilder, ImmutableArray <KeyValuePair <BoundExpression, BoundExpression> > bindings) { if (!bindings.IsDefaultOrEmpty) { foreach (var kv in bindings) { var source = kv.Key; var dest = kv.Value; var rewriter = this.LocalRewriter; sectionBuilder.Add(_factory.ExpressionStatement( rewriter.MakeStaticAssignmentOperator( _factory.Syntax, rewriter.VisitExpression(dest), rewriter.VisitExpression(source), RefKind.None, dest.Type, false))); } } }
private static BoundStatement ConstructThrowInvalidOperationExceptionHelperCall(SyntheticBoundNodeFactory factory) { Debug.Assert(factory.ModuleBuilderOpt is not null); var module = factory.ModuleBuilderOpt !; var diagnosticSyntax = factory.CurrentFunction.GetNonNullSyntaxNode(); var diagnostics = factory.Diagnostics.DiagnosticBag; Debug.Assert(diagnostics is not null); var throwMethod = module.EnsureThrowInvalidOperationExceptionExists(diagnosticSyntax, factory, diagnostics); var call = factory.Call( receiver: null, throwMethod); return(factory.HiddenSequencePoint(factory.ExpressionStatement(call))); }
private static BoundStatement ConstructNullCheckHelperCall(ParameterSymbol parameter, ref MethodSymbol?throwIfNullMethod, SyntheticBoundNodeFactory factory) { if (throwIfNullMethod is null) { var module = factory.ModuleBuilderOpt !; var diagnosticSyntax = factory.CurrentFunction.GetNonNullSyntaxNode(); var diagnostics = factory.Diagnostics.DiagnosticBag; Debug.Assert(diagnostics is not null); throwIfNullMethod = module.EnsureThrowIfNullFunctionExists(diagnosticSyntax, factory, diagnostics); } var call = factory.Call( receiver: null, throwIfNullMethod, arg0: factory.Convert(factory.SpecialType(SpecialType.System_Object), factory.Parameter(parameter)), arg1: factory.StringLiteral(parameter.Name)); return(factory.HiddenSequencePoint(factory.ExpressionStatement(call))); }
private static BoundBlock PrependImplicitInitializations(BoundBlock body, MethodSymbol method, ImmutableArray <FieldSymbol> implicitlyInitializedFields, TypeCompilationState compilationState, BindingDiagnosticBag diagnostics) { Debug.Assert(method.MethodKind == MethodKind.Constructor); Debug.Assert(method.ContainingType.IsStructType()); var F = new SyntheticBoundNodeFactory(method, body.Syntax, compilationState, diagnostics); var builder = ArrayBuilder <BoundStatement> .GetInstance(implicitlyInitializedFields.Length); foreach (var field in implicitlyInitializedFields) { builder.Add( F.ExpressionStatement( F.AssignmentExpression( F.Field(F.This(), field), F.Default(field.Type)))); } var initializations = F.HiddenSequencePoint(F.Block(builder.ToImmutableAndFree())); return(body.Update(body.Locals, body.LocalFunctions, body.Statements.Insert(index: 0, initializations))); }
/// <summary> /// Add a branch in the lowered decision tree to a label for a matched /// pattern, and then produce a statement for the target of that branch /// that binds the pattern variables. /// </summary> /// <param name="bindings">The source/destination pairs for the assignments</param> /// <param name="addBindings">A builder to which the label and binding assignments are added</param> private void AddBindingsForCase( ImmutableArray <KeyValuePair <BoundExpression, BoundExpression> > bindings, ArrayBuilder <BoundStatement> addBindings) { var patternMatched = _factory.GenerateLabel("patternMatched"); _loweredDecisionTree.Add(_factory.Goto(patternMatched)); // Hide the code that binds pattern variables in a hidden sequence point addBindings.Add(_factory.HiddenSequencePoint()); addBindings.Add(_factory.Label(patternMatched)); if (!bindings.IsDefaultOrEmpty) { foreach (var kv in bindings) { var loweredRight = kv.Key; var loweredLeft = kv.Value; addBindings.Add(_factory.ExpressionStatement( _localRewriter.MakeStaticAssignmentOperator( _factory.Syntax, loweredLeft, loweredRight, RefKind.None, loweredLeft.Type, false))); } } }
private BoundStatement InitializeFixedStatementArrayLocal( LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol arrayTemp) { // From ExpressionBinder::BindPtrToArray: // (((temp = array) != null && temp.Length > 0) ? loc = &temp[0] : loc = null) // NOTE: The assignment needs to be inside the EK_QUESTIONMARK. See Whidbey bug #397859. // We can't do loc = (... ? ... : ...) since the CLR type of &temp[0] is a managed // pointer and null is a UIntPtr - which confuses the JIT. We can't just convert // &temp[0] to UIntPtr with a conv.u instruction because then if a GC occurs between // the time of the cast and the assignment to the local, we're toast. TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; arrayTemp = factory.SynthesizedLocal(initializerType); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)arrayTemp.Type; TypeSymbol arrayElementType = arrayType.ElementType; int arrayRank = arrayType.Rank; // NOTE: we pin the pointer, not the array. Debug.Assert(!arrayTemp.IsPinned); Debug.Assert(localSymbol.IsPinned); //(temp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(arrayTemp), initializerExpr); //(temp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayRank == 1) { lengthCall = factory.ArrayLength(factory.Local(arrayTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(arrayTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(factory.Local(arrayTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(arrayTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of pinned local. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, isFixedStatementAddressOf: true, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return(AddLocalDeclarationSequencePointIfNecessary(fixedInitializer.Syntax.Parent.Parent, localSymbol, localInit)); }
private BoundStatement InitializeFixedStatementArrayLocal( LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol arrayTemp) { // From ExpressionBinder::BindPtrToArray: // (((temp = array) != null && temp.Length > 0) ? loc = &temp[0] : loc = null) // NOTE: The assignment needs to be inside the EK_QUESTIONMARK. See Whidbey bug #397859. // We can't do loc = (... ? ... : ...) since the CLR type of &temp[0] is a managed // pointer and null is a UIntPtr - which confuses the JIT. We can't just convert // &temp[0] to UIntPtr with a conv.u instruction because then if a GC occurs between // the time of the cast and the assignment to the local, we're toast. TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; arrayTemp = factory.SynthesizedLocal(initializerType); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)arrayTemp.Type; TypeSymbol arrayElementType = arrayType.ElementType; int arrayRank = arrayType.Rank; // NOTE: we pin the pointer, not the array. Debug.Assert(!arrayTemp.IsPinned); Debug.Assert(localSymbol.IsPinned); //(temp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(arrayTemp), initializerExpr); //(temp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayRank == 1) { lengthCall = factory.ArrayLength(factory.Local(arrayTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(arrayTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(factory.Local(arrayTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(arrayTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of pinned local. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, isFixedStatementAddressOf: true, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return AddLocalDeclarationSequencePointIfNecessary(fixedInitializer.Syntax.Parent.Parent, localSymbol, localInit); }
private BoundExpression Spill( BoundSpillSequenceBuilder builder, BoundExpression expression, RefKind refKind = RefKind.None, bool sideEffectsOnly = false) { Debug.Assert(builder != null); while (true) { switch (expression.Kind) { case BoundKind.ArrayInitialization: Debug.Assert(refKind == RefKind.None); Debug.Assert(!sideEffectsOnly); var arrayInitialization = (BoundArrayInitialization)expression; var newInitializers = VisitExpressionList(ref builder, arrayInitialization.Initializers, forceSpill: true); return(arrayInitialization.Update(newInitializers)); case BoundKind.ArgListOperator: Debug.Assert(refKind == RefKind.None); Debug.Assert(!sideEffectsOnly); var argumentList = (BoundArgListOperator)expression; var newArgs = VisitExpressionList(ref builder, argumentList.Arguments, argumentList.ArgumentRefKindsOpt, forceSpill: true); return(argumentList.Update(newArgs, argumentList.ArgumentRefKindsOpt, argumentList.Type)); case SpillSequenceBuilderKind: var sequenceBuilder = (BoundSpillSequenceBuilder)expression; builder.Include(sequenceBuilder); expression = sequenceBuilder.Value; continue; case BoundKind.Sequence: // neither the side-effects nor the value of the sequence contains await // (otherwise it would be converted to a SpillSequenceBuilder). if (refKind != RefKind.None) { return(expression); } goto default; case BoundKind.ThisReference: case BoundKind.BaseReference: if (refKind != RefKind.None || expression.Type.IsReferenceType) { return(expression); } goto default; case BoundKind.Parameter: if (refKind != RefKind.None) { return(expression); } goto default; case BoundKind.Local: var local = (BoundLocal)expression; if (local.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.Spill || refKind != RefKind.None) { return(local); } goto default; case BoundKind.FieldAccess: var field = (BoundFieldAccess)expression; var fieldSymbol = field.FieldSymbol; if (fieldSymbol.IsStatic) { // no need to spill static fields if used as locations or if readonly if (refKind != RefKind.None || fieldSymbol.IsReadOnly) { return(field); } goto default; } if (refKind == RefKind.None) { goto default; } var receiver = Spill(builder, field.ReceiverOpt, fieldSymbol.ContainingType.IsValueType ? refKind : RefKind.None); return(field.Update(receiver, fieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type)); case BoundKind.Literal: case BoundKind.TypeExpression: return(expression); case BoundKind.ConditionalReceiver: // we will rewrite this as a part of rewriting whole LoweredConditionalAccess // later, if needed return(expression); default: if (expression.Type.IsVoidType() || sideEffectsOnly) { builder.AddStatement(_F.ExpressionStatement(expression)); return(null); } else { BoundAssignmentOperator assignToTemp; var replacement = _F.StoreToTemp( expression, out assignToTemp, refKind: refKind, kind: SynthesizedLocalKind.Spill, syntaxOpt: _F.Syntax); builder.AddLocal(replacement.LocalSymbol); builder.AddStatement(_F.ExpressionStatement(assignToTemp)); return(replacement); } } } }
/// <summary> /// <![CDATA[ /// fixed(int* ptr = arr){ ... } == becomes ===> /// /// pinned int[] pinnedTemp = arr; // pinning managed ref /// int* ptr = pinnedTemp != null && pinnedTemp.Length != 0 /// (int*)&pinnedTemp[0]: // unsafe cast to unmanaged ptr /// 0; /// . . . /// ]]> /// </summary> private BoundStatement InitializeFixedStatementArrayLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; pinnedTemp = factory.SynthesizedLocal(initializerType, isPinned: true); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)pinnedTemp.Type; TypeWithAnnotations arrayElementType = arrayType.ElementTypeWithAnnotations; // NOTE: we pin the array, not the pointer. Debug.Assert(pinnedTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); //(pinnedTemp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(pinnedTemp), initializerExpr); //(pinnedTemp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayType.IsSZArray) { lengthCall = factory.ArrayLength(factory.Local(pinnedTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(pinnedTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(factory.Local(pinnedTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(pinnedTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of the pointer. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, false, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return(InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, localInit)); }
private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node) { var expression = _localRewriter.VisitExpression(node.Expression); var result = ArrayBuilder <BoundStatement> .GetInstance(); // EnC: We need to insert a hidden sequence point to handle function remapping in case // the containing method is edited while methods invoked in the expression are being executed. if (!node.WasCompilerGenerated && _localRewriter.Instrument) { var instrumentedExpression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory); if (expression.ConstantValue == null) { expression = instrumentedExpression; } else { // If the expression is a constant, we leave it alone (the decision tree lowering code needs // to see that constant). But we add an additional leading statement with the instrumented expression. result.Add(_factory.ExpressionStatement(instrumentedExpression)); } } // output the decision tree part LowerPatternSwitch(expression, node, result); // if the endpoint is reachable, we exit the switch if (!node.IsComplete) { result.Add(_factory.Goto(node.BreakLabel)); } // at this point the end of result is unreachable. _declaredTemps.AddRange(node.InnerLocals); // output the sections of code foreach (var section in node.SwitchSections) { // Lifetime of these locals is expanded to the entire switch body. _declaredTemps.AddRange(section.Locals); // Start with the part of the decision tree that is in scope of the section variables. // Its endpoint is not reachable (it jumps back into the decision tree code). var sectionSyntax = (SyntaxNode)section.Syntax; var sectionBuilder = _switchSections[sectionSyntax]; // Add labels corresponding to the labels of the switch section. foreach (var label in section.SwitchLabels) { sectionBuilder.Add(_factory.Label(label.Label)); } // Add the translated body of the switch section sectionBuilder.AddRange(_localRewriter.VisitList(section.Statements)); sectionBuilder.Add(_factory.Goto(node.BreakLabel)); ImmutableArray <BoundStatement> statements = sectionBuilder.ToImmutableAndFree(); if (section.Locals.IsEmpty) { result.Add(_factory.StatementList(statements)); } else { result.Add(new BoundScope(section.Syntax, section.Locals, statements)); } // at this point the end of result is unreachable. } result.Add(_factory.Label(node.BreakLabel)); BoundStatement translatedSwitch = _factory.Block(_declaredTemps.ToImmutableArray(), node.InnerLocalFunctions, result.ToImmutableAndFree()); // Only add instrumentation (such as a sequence point) if the node is not compiler-generated. if (!node.WasCompilerGenerated && _localRewriter.Instrument) { translatedSwitch = _localRewriter._instrumenter.InstrumentPatternSwitchStatement(node, translatedSwitch); } return(translatedSwitch); }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundNode PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var proxyFields = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance(); foreach (var local in locals) { if (!VariablesCaptured.Contains(local)) { continue; } CapturedSymbolReplacement proxy; if (proxies.TryGetValue(local, out proxy)) { // All of the user-declared variables have pre-allocated proxies var field = proxy.HoistedField; Debug.Assert((object)field != null); Debug.Assert(local.DeclarationKind != LocalDeclarationKind.CompilerGenerated); // temps have lazily allocated proxies if (local.DeclarationKind != LocalDeclarationKind.CompilerGenerated) { proxyFields.Add(field); } } else { if (local.RefKind == RefKind.None) { SynthesizedFieldSymbolBase field = MakeHoistedTemp(local, TypeMap.SubstituteType(local.Type)); proxy = new CapturedToFrameSymbolReplacement(field); proxies.Add(local, proxy); } // ref temporary variables have proxies that are allocated on demand // See VisitAssignmentOperator. } } var translatedStatement = wrapped(); // produce code to free (e.g. mark as available for reuse) and clear (e.g. set to null) the proxies for any temps for these locals var clearTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); foreach (var local in locals) { ArrayBuilder <SynthesizedFieldSymbolBase> frees; if (freeTempsMap.TryGetValue(local, out frees)) { Debug.Assert(local.DeclarationKind == LocalDeclarationKind.CompilerGenerated); // only temps are managed this way freeTempsMap.Remove(local); foreach (var field in frees) { if (MightContainReferences(field.Type)) { clearTemps.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } FreeTemp(field); } frees.Free(); } } if (clearTemps.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(clearTemps.Select(e => F.ExpressionStatement(e)).AsImmutable <BoundStatement>()) ); } clearTemps.Free(); // wrap the node in an iterator scope for debugging if (proxyFields.Count != 0) { translatedStatement = new BoundIteratorScope(F.Syntax, proxyFields.ToImmutable(), translatedStatement); } proxyFields.Free(); return(translatedStatement); }