/// <summary> /// Visit try statement /// </summary> /// <param name="tryStatementAst"></param> /// <returns></returns> public object VisitTryStatement(TryStatementAst tryStatementAst) { if (tryStatementAst == null) return null; // We don't attempt to accurately model flow in a try catch because every statement // can flow to each catch. Instead, we'll assume the try block is not executed (because the very first statement // may throw), and have the data flow assume the block before the try is all that can reach the catches and finally. var blockBeforeTry = _currentBlock; _currentBlock = new Block(); blockBeforeTry.FlowsTo(_currentBlock); tryStatementAst.Body.Visit(this.Decorator); Block lastBlockInTry = _currentBlock; var finallyFirstBlock = tryStatementAst.Finally == null ? null : new Block(); Block finallyLastBlock = null; // This is the first block after all the catches and finally (if present). var afterTry = new Block(); bool isCatchAllPresent = false; foreach (var catchAst in tryStatementAst.CatchClauses) { if (catchAst.IsCatchAll) { isCatchAllPresent = true; } // Any statement in the try block could throw and reach the catch, so assume the worst (from a data // flow perspective) and make the predecessor to the catch the block before entering the try. _currentBlock = new Block(); blockBeforeTry.FlowsTo(_currentBlock); catchAst.Visit(this.Decorator); _currentBlock.FlowsTo(finallyFirstBlock ?? afterTry); } if (finallyFirstBlock != null) { lastBlockInTry.FlowsTo(finallyFirstBlock); _currentBlock = finallyFirstBlock; tryStatementAst.Finally.Visit(this.Decorator); _currentBlock.FlowsTo(afterTry); finallyLastBlock = _currentBlock; // For finally block, there are 2 cases: when try-body throw and when it doesn't. // For these two cases value of 'finallyLastBlock._throws' would be different. if (!isCatchAllPresent) { // This flow exist only, if there is no catch for all exceptions. blockBeforeTry.FlowsTo(finallyFirstBlock); var rethrowAfterFinallyBlock = new Block(); finallyLastBlock.FlowsTo(rethrowAfterFinallyBlock); rethrowAfterFinallyBlock._throws = true; rethrowAfterFinallyBlock.FlowsTo(_exitBlock); } // This flow always exists. finallyLastBlock.FlowsTo(afterTry); } else { lastBlockInTry.FlowsTo(afterTry); } _currentBlock = afterTry; return null; }
/// <summary> /// Visit binary expression /// </summary> /// <param name="binaryExpressionAst"></param> /// <returns></returns> public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst) { if (binaryExpressionAst == null) return null; if (binaryExpressionAst.Operator == TokenKind.And || binaryExpressionAst.Operator == TokenKind.Or) { // Logical and/or are short circuit operators, so we need to simulate the control flow. The // left operand is always evaluated, visit it's expression in the current block. binaryExpressionAst.Left.Visit(this.Decorator); // The right operand is conditionally evaluated. We aren't generating any code here, just // modeling the flow graph, so we just visit the right operand in a new block, and have // both the current and new blocks both flow to a post-expression block. var targetBlock = new Block(); var nextBlock = new Block(); _currentBlock.FlowsTo(targetBlock); _currentBlock.FlowsTo(nextBlock); _currentBlock = nextBlock; binaryExpressionAst.Right.Visit(this.Decorator); _currentBlock.FlowsTo(targetBlock); _currentBlock = targetBlock; } else { binaryExpressionAst.Left.Visit(this.Decorator); binaryExpressionAst.Right.Visit(this.Decorator); } return null; }
internal void GenerateDoLoop(LoopStatementAst loopStatement) { // We model the flow graph like this: // :RepeatTarget // loop body // // break -> goto BreakTarget // // continue -> goto ContinueTarget // :ContinueTarget // if (condition) // { // goto RepeatTarget // } // :BreakTarget var continueBlock = new Block(); var bodyBlock = new Block(); var breakBlock = new Block(); var gotoRepeatTargetBlock = new Block(); _loopTargets.Add(new LoopGotoTargets(loopStatement.Label ?? "", breakBlock, continueBlock)); _currentBlock.FlowsTo(bodyBlock); _currentBlock = bodyBlock; loopStatement.Body.Visit(this.Decorator); _currentBlock.FlowsTo(continueBlock); _currentBlock = continueBlock; loopStatement.Condition.Visit(this.Decorator); _currentBlock.FlowsTo(breakBlock); _currentBlock.FlowsTo(gotoRepeatTargetBlock); _currentBlock = gotoRepeatTargetBlock; _currentBlock.FlowsTo(bodyBlock); _currentBlock = breakBlock; _loopTargets.RemoveAt(_loopTargets.Count - 1); }
internal void GenerateWhileLoop(string loopLabel, Action generateCondition, Action generateLoopBody, Ast continueAction = null) { // We model the flow graph like this (if continueAction is null, the first part is slightly different): // goto L // :ContinueTarget // continueAction // :L // if (condition) // { // loop body // // break -> goto BreakTarget // // continue -> goto ContinueTarget // goto ContinueTarget // } // :BreakTarget var continueBlock = new Block(); if (continueAction != null) { var blockAfterContinue = new Block(); // Represent the goto over the condition before the first iteration. _currentBlock.FlowsTo(blockAfterContinue); _currentBlock = continueBlock; continueAction.Visit(this.Decorator); _currentBlock.FlowsTo(blockAfterContinue); _currentBlock = blockAfterContinue; } else { _currentBlock.FlowsTo(continueBlock); _currentBlock = continueBlock; } var bodyBlock = new Block(); var breakBlock = new Block(); // Condition can be null from an uncommon for loop: for() {} if (generateCondition != null) { generateCondition(); _currentBlock.FlowsTo(breakBlock); } _loopTargets.Add(new LoopGotoTargets(loopLabel ?? "", breakBlock, continueBlock)); _currentBlock.FlowsTo(bodyBlock); _currentBlock = bodyBlock; generateLoopBody(); _currentBlock.FlowsTo(continueBlock); _currentBlock = breakBlock; _loopTargets.RemoveAt(_loopTargets.Count - 1); }
/// <summary> /// Visit switch statement /// </summary> /// <param name="switchStatementAst"></param> /// <returns></returns> public object VisitSwitchStatement(SwitchStatementAst switchStatementAst) { if (switchStatementAst == null) return null; Action generateCondition = () => { switchStatementAst.Condition.Visit(this.Decorator); // $switch is set after evaluating the condition. _currentBlock.AddAst(new AssignmentTarget(SpecialVars.@switch, typeof(IEnumerator))); }; Action switchBodyGenerator = () => { bool hasDefault = (switchStatementAst.Default != null); Block afterStmt = new Block(); int clauseCount = switchStatementAst.Clauses.Count; for (int i = 0; i < clauseCount; i++) { var clause = switchStatementAst.Clauses[i]; Block clauseBlock = new Block(); bool isLastClause = (i == (clauseCount - 1) && !hasDefault); Block nextBlock = isLastClause ? afterStmt : new Block(); clause.Item1.Visit(this.Decorator); _currentBlock.FlowsTo(nextBlock); _currentBlock.FlowsTo(clauseBlock); _currentBlock = clauseBlock; clause.Item2.Visit(this.Decorator); if (!isLastClause) { _currentBlock.FlowsTo(nextBlock); _currentBlock = nextBlock; } } if (hasDefault) { // If any clause was executed, we skip the default, so there is always a branch over the default. _currentBlock.FlowsTo(afterStmt); switchStatementAst.Default.Visit(this.Decorator); } _currentBlock.FlowsTo(afterStmt); _currentBlock = afterStmt; }; GenerateWhileLoop(switchStatementAst.Label, generateCondition, switchBodyGenerator); return null; }
/// <summary> /// Visit if statement /// </summary> /// <param name="ifStmtAst"></param> /// <returns></returns> public object VisitIfStatement(IfStatementAst ifStmtAst) { if (ifStmtAst == null) return null; Block afterStmt = new Block(); if (ifStmtAst.ElseClause == null) { // There is no else, flow can go straight to afterStmt. _currentBlock.FlowsTo(afterStmt); } int clauseCount = ifStmtAst.Clauses.Count; for (int i = 0; i < clauseCount; i++) { var clause = ifStmtAst.Clauses[i]; bool isLastClause = (i == (clauseCount - 1) && ifStmtAst.ElseClause == null); Block clauseBlock = new Block(); Block nextBlock = isLastClause ? afterStmt : new Block(); clause.Item1.Visit(this); _currentBlock.FlowsTo(clauseBlock); _currentBlock.FlowsTo(nextBlock); _currentBlock = clauseBlock; clause.Item2.Visit(this); _currentBlock.FlowsTo(afterStmt); _currentBlock = nextBlock; } if (ifStmtAst.ElseClause != null) { ifStmtAst.ElseClause.Visit(this); _currentBlock.FlowsTo(afterStmt); } _currentBlock = afterStmt; return null; }
/// <summary> /// Visit script block /// </summary> /// <param name="scriptBlockAst"></param> /// <returns></returns> public object VisitScriptBlock(ScriptBlockAst scriptBlockAst) { if (scriptBlockAst == null) return null; _currentBlock = _entryBlock; if (scriptBlockAst.DynamicParamBlock != null) { scriptBlockAst.DynamicParamBlock.Visit(this.Decorator); } if (scriptBlockAst.BeginBlock != null) { scriptBlockAst.BeginBlock.Visit(this.Decorator); } if (scriptBlockAst.ProcessBlock != null) { scriptBlockAst.ProcessBlock.Visit(this.Decorator); } if (scriptBlockAst.EndBlock != null) { scriptBlockAst.EndBlock.Visit(this.Decorator); } _currentBlock.FlowsTo(_exitBlock); return null; }