/// <summary> /// Visits block statements and its edge to next block. /// </summary> protected virtual void VisitCFGBlockInternal(BoundBlock x) { VisitCFGBlockStatements(x); if (x.NextEdge != null) x.NextEdge.Visit(this); }
/// <summary> /// Subscribe a block to be analysed when the exit block is reached and the routine return value changes. /// </summary> internal void Subscribe(BoundBlock x) { if (_subscribers == null) _subscribers = new HashSet<BoundBlock>(); _subscribers.Add(x); }
void VisitCFGBlockStatements(BoundBlock x) { for (int i = 0; i < x.Statements.Count; i++) { Accept(x.Statements[i]); } }
public virtual void VisitBlockStatement(Graph.BoundBlock x) { for (int i = 0; i < x.Statements.Count; i++) { Accept(x.Statements[i]); } }
protected virtual TResult DefaultVisitBlock(BoundBlock x) => default;
public virtual void VisitBlockStatement(Graph.BoundBlock x) { x.Statements.ForEach(Accept); }
public BreakTargetScope(BoundBlock breakBlock, BoundBlock continueBlock) { BreakTarget = breakBlock; ContinueTarget = continueBlock; }
internal TryCatchEdge(BoundBlock source, BoundBlock body, CatchBlock[] catchBlocks, BoundBlock finallyBlock, BoundBlock endBlock) : base(source) { _body = body; _catchBlocks = catchBlocks; _finallyBlock = finallyBlock; _end = endBlock; Connect(source); }
internal LeaveEdge(BoundBlock source, BoundBlock target) :base(source, target) { }
/// <summary> /// Helper method that merges state with the target block and determines whether to continue by visiting the target block. /// </summary> /// <param name="state">Locals state in which we are entering the target.</param> /// <param name="target">Target block.</param> /// <remarks>Only for traversing into blocks within the same routine (same type context).</remarks> private void TraverseToBlock(FlowState/*!*/state, BoundBlock/*!*/target) { Contract.ThrowIfNull(state); // state should be already set by previous block var targetState = target.FlowState; if (targetState != null) { Debug.Assert(targetState.FlowContext == state.FlowContext); // block was visited already, // merge and check whether state changed state = state.Merge(targetState); // merge states into new one if (state.Equals(targetState)) { return; // state convergated, we don't have to analyse target block again } } else { // block was not visited yet state = state.Clone(); // copy state into new one } // update target block state target.FlowState = state; // _worklist.Enqueue(target); }
public override void VisitSwitchStmt(SwitchStmt x) { var items = x.SwitchItems; if (items == null || items.Length == 0) { return; } // get bound item for switch value & connect potential pre-switch-value blocks var boundBagForSwitchValue = _binder.BindWholeExpression(x.SwitchValue, BoundAccess.Read); ConnectBoundItemsBagBlocksToCurrentBlock(boundBagForSwitchValue); var switchValue = boundBagForSwitchValue.BoundElement; var end = NewBlock(); bool hasDefault = false; var cases = new List <CaseBlock>(items.Length); for (int i = 0; i < items.Length; i++) { cases.Add(NewBlock(items[i])); hasDefault |= (items[i] is DefaultItem); } if (!hasDefault) { // create implicit default: cases.Add(NewBlock(new DefaultItem(x.Span, Array.Empty <Statement>()))); } // if switch value isn't a constant & there're case values with preBoundStatements // -> the switch value might get evaluated multiple times (see SwitchEdge.Generate) -> preemptively evaluate and cache it if (!switchValue.IsConstant() && !cases.All(c => c.CaseValue.IsOnlyBoundElement)) { var result = GeneratorSemanticsBinder.CreateAndAssignSynthesizedVariable(switchValue, BoundAccess.Read, $"<switchValueCacher>{x.Span}"); switchValue = result.BoundExpr; _current.Add(new BoundExpressionStatement(result.Assignment)); } // SwitchEdge // Connects _current to cases var edge = new SwitchEdge(_current, switchValue, cases.ToImmutableArray(), end); _current = WithNewOrdinal(cases[0]); OpenBreakScope(end, end); // NOTE: inside switch, Continue ~ Break for (int i = 0; i < cases.Count; i++) { OpenScope(_current); if (i < items.Length) { items[i].Statements.ForEach(VisitElement); // any break will connect block to end } CloseScope(); _current = WithNewOrdinal(Connect(_current, (i == cases.Count - 1) ? end : cases[i + 1])); } CloseBreakScope(); Debug.Assert(_current == end); }
private void OpenScope(BoundBlock block, LocalScope scope = LocalScope.Code) { _scopes.Push(new LocalScopeInfo(block, scope)); }
public LocalScopeInfo(BoundBlock firstBlock, LocalScope scope) { this.FirstBlock = firstBlock; this.Scope = scope; }
private BoundBlock /*!*/ Leave(BoundBlock /*!*/ source, BoundBlock /*!*/ target) { new LeaveEdge(source, target); return(target); }
private BoundBlock /*!*/ Connect(BoundBlock /*!*/ source, BoundBlock /*!*/ target) { new SimpleEdge(source, target); return(target); }
private BoundBlock /*!*/ Connect(BoundBlock /*!*/ source, BoundBlock /*!*/ ifTarget, BoundBlock /*!*/ elseTarget, Expression condition, bool isloop = false) { if (condition != null) { // bind condition expression & connect pre-condition blocks to source var boundConditionBag = _binder.BindWholeExpression(condition, BoundAccess.Read); source = ConnectBoundItemsBagBlocks(boundConditionBag, source); new ConditionalEdge(source, ifTarget, elseTarget, boundConditionBag.BoundElement) { IsLoop = isloop, }; return(ifTarget); } else { // jump to ifTarget if there is no condition (always true) // e.g. for (;;) { [ifTarget] } return(Connect(source, ifTarget)); } }
private void ConnectBoundItemsBagBlocksToCurrentBlock <T>(BoundItemsBag <T> bag) where T : class, IPhpOperation { _current = ConnectBoundItemsBagBlocks(bag, _current); }
/// <summary> /// Called to initialize <see cref="VisitCFGBlock"/> call. /// Sets <see cref="_state"/> to known initial block state. /// </summary> protected virtual void VisitCFGBlockInit(BoundBlock/*!*/x) { Contract.ThrowIfNull(x.FlowState); // state should be already set by previous edge _state = x.FlowState.Clone(); // TFlowState for the statements in the block this.CurrentBlock = x; }
public override void VisitCFGBlock(BoundBlock x) { VisitCFGBlockInit(x); VisitCFGBlockInternal(x); // modifies _state, traverses to the edge }
public override void VisitTryStmt(TryStmt x) { // try { // x.Body // } // catch (E1) { body } // catch (E2) { body } // finally { body } // end var end = NewBlock(); var body = NewBlock(); // init catch blocks and finally block var catchBlocks = ImmutableArray <CatchBlock> .Empty; if (x.Catches != null) { var catchBuilder = ImmutableArray.CreateBuilder <CatchBlock>(x.Catches.Length); for (int i = 0; i < x.Catches.Length; i++) { catchBuilder.Add(NewBlock(x.Catches[i])); } catchBlocks = catchBuilder.MoveToImmutable(); } BoundBlock finallyBlock = null; if (x.FinallyItem != null) { finallyBlock = NewBlock(); } // TryCatchEdge // Connects _current to body, catch blocks and finally var edge = new TryCatchEdge(_current, body, catchBlocks, finallyBlock, end); var oldstates0 = _binder.StatesCount; // build try body OpenTryScope(edge); OpenScope(body, LocalScope.Try); _current = WithNewOrdinal(body); VisitElement(x.Body); CloseScope(); CloseTryScope(); _current = Leave(_current, finallyBlock ?? end); var oldstates1 = _binder.StatesCount; // built catches for (int i = 0; i < catchBlocks.Length; i++) { _current = WithOpenScope(WithNewOrdinal(catchBlocks[i]), LocalScope.Catch); VisitElement(x.Catches[i].Body); CloseScope(); _current = Leave(_current, finallyBlock ?? end); } // build finally var oldReturnCount = _returnCounter; if (finallyBlock != null) { _current = WithOpenScope(WithNewOrdinal(finallyBlock), LocalScope.Finally); VisitElement(x.FinallyItem.Body); CloseScope(); _current = Leave(_current, end); } // if ((oldstates0 != oldstates1 && finallyBlock != null) || // yield in "try" with "finally" block oldstates1 != _binder.StatesCount || // yield in "catch" or "finally" oldReturnCount != _returnCounter) // return in "finally" { // catch or finally introduces new states to the state machine // or there is "return" in finally block: // catch/finally must not be handled by CLR edge.EmitCatchFinallyOutsideScope = true; } // _current == end _current.Ordinal = NewOrdinal(); }
internal SimpleEdge(BoundBlock source, BoundBlock target) : base(source) { Debug.Assert(source != target); _target = target; Connect(source); }
internal ForeachMoveNextEdge(BoundBlock/*!*/source, BoundBlock/*!*/body, BoundBlock/*!*/end, ForeachEnumereeEdge/*!*/enumereeEdge, BoundReferenceExpression keyVar, BoundReferenceExpression/*!*/valueVar) : base(source) { Contract.ThrowIfNull(body); Contract.ThrowIfNull(end); Contract.ThrowIfNull(enumereeEdge); _body = body; _end = end; _enumereeEdge = enumereeEdge; _keyVariable = keyVar; _valueVariable = valueVar; Connect(source); }
internal ConditionalEdge(BoundBlock source, BoundBlock @true, BoundBlock @false, BoundExpression cond) : base(source) { Debug.Assert(@true != @false); _true = @true; _false = @false; _condition = cond; Connect(source); }
internal Edge(BoundBlock/*!*/source) { Contract.ThrowIfNull(source); }
internal ForeachEnumereeEdge(BoundBlock/*!*/source, BoundBlock/*!*/target, BoundExpression/*!*/enumeree, bool aliasedValues) : base(source, target) { Contract.ThrowIfNull(enumeree); _enumeree = enumeree; _aliasedValues = aliasedValues; }
public virtual TResult VisitCFGBlock(BoundBlock x) => DefaultVisitBlock(x);
internal SwitchEdge(BoundBlock source, BoundExpression switchValue, CaseBlock[] caseBlocks, BoundBlock endBlock) : base(source) { Contract.ThrowIfNull(caseBlocks); _switchValue = switchValue; _caseBlocks = caseBlocks; _end = endBlock; Connect(source); }
public virtual void VisitCFGBlock(BoundBlock x) { VisitCFGBlockInternal(x); }
protected void Connect(BoundBlock/*!*/source) { source.NextEdge = this; }
void AnalyzeBlock(BoundBlock block) // TODO: driver { // TODO: pool of CFGAnalysis // TODO: async // TODO: in parallel block.Accept(AnalysisFactory()); }
/// <summary> /// Enqueues the standalone expression for analysis. /// </summary> void EnqueueExpression(BoundExpression expression, TypeRefContext/*!*/ctx, NamingContext naming) { Contract.ThrowIfNull(expression); Contract.ThrowIfNull(ctx); var dummy = new BoundBlock() { FlowState = new FlowState(new FlowContext(ctx, null)), Naming = naming }; dummy.Add(new BoundExpressionStatement(expression)); _worklist.Enqueue(dummy); }
private ControlFlowGraph(BoundBlock/*!*/start, BoundBlock/*!*/exit, BoundBlock exception, LabelBlockState[] labels, List<BoundBlock> unreachable) { Contract.ThrowIfNull(start); Contract.ThrowIfNull(exit); _start = start; _exit = exit; //_exception = exception; _labels = labels; _unrecachable = unreachable ?? new List<BoundBlock>(); }
public virtual TResult VisitBlockStatement(Graph.BoundBlock x) => DefaultVisitOperation(x);
/// <summary>Visits given block.</summary> protected TResult Accept(BoundBlock x) => (x != null) ? x.Accept(this) : default;