Ejemplo n.º 1
0
        /// <summary>
        /// Copies the closure initialization into the contractinitializer and preambleBlock (if non-null)
        /// </summary>
        internal static int MovePastClosureInit(Method m, Block firstBlock, ContractNodes contractNodes,
            StatementList contractInitializer, Block preambleBlock, int currentIndex,
            ref StackDepthTracker dupStackTracker, out TypeNode closureType)
        {
            int indexForClosureCreationStatement = currentIndex;
            
            closureType = null;
            Local introducedClosureLocal = null;
            
            while (indexForClosureCreationStatement < firstBlock.Statements.Count)
            {
                var closureCreationCandidate = firstBlock.Statements[indexForClosureCreationStatement];
                closureType = IsClosureCreation(m, closureCreationCandidate);
                
                if (closureType != null) break;
                
                if (contractNodes.IsContractOrValidatorOrAbbreviatorCall(closureCreationCandidate))
                {
                    // found contracts before closure creation, so get out
                    return currentIndex;
                }

                if (closureCreationCandidate != null && closureCreationCandidate.SourceContext.IsValid)
                    return currentIndex;
                
                indexForClosureCreationStatement++;
            }

            if (closureType != null && indexForClosureCreationStatement < firstBlock.Statements.Count)
            {
                // then there is a set of statements to add to the preamble block
                // up to and including "local := new ClosureClass();"
                for (int i = currentIndex; i <= indexForClosureCreationStatement; i++)
                {
                    if (firstBlock.Statements[i] == null) continue;

                    if (preambleBlock != null)
                    {
                        preambleBlock.Statements.Add(firstBlock.Statements[i]);
                    }

                    Local existingClosureLocal;
                    if (IsClosureCreation(m, firstBlock.Statements[i], out existingClosureLocal))
                    {
                        if (existingClosureLocal == null)
                        {
                            // introduce one
                            ExpressionStatement estmt = firstBlock.Statements[i] as ExpressionStatement;
                            if (estmt != null)
                            {
                                introducedClosureLocal = new Local(closureType);
                                contractInitializer.Add(
                                    new AssignmentStatement(introducedClosureLocal, (Expression) estmt.Expression.Clone()));
                            }
                            else
                            {
                                contractInitializer.Add((Statement) firstBlock.Statements[i].Clone());
                            }
                        }
                        else
                        {
                            contractInitializer.Add((Statement) firstBlock.Statements[i].Clone());
                        }
                    }
                    else
                    {
                        contractInitializer.Add((Statement) firstBlock.Statements[i].Clone());
                    }

                    if (preambleBlock != null)
                    {
                        firstBlock.Statements[i] = null;
                        // need to null them out so search below can be done starting at beginning of m's body
                    }
                }

                // Some number of assignment statements of the form "local.f := f;" where "f" is a parameter
                // that is captured by the closure.
                //
                // Roslyn generates code as follows:
                //   new Closure()
                //   dup
                //   ldarg f
                //   stfld f
                //   dup
                //   ldarg q
                //   stfld q
                //   dup...
                // 

                int endOfAssignmentsToClosureFields = indexForClosureCreationStatement + 1;
                for (; endOfAssignmentsToClosureFields < firstBlock.Statements.Count; endOfAssignmentsToClosureFields++)
                {
                    Statement s = firstBlock.Statements[endOfAssignmentsToClosureFields];
                    if (s == null) continue;

                    if (s.NodeType == NodeType.Nop) continue;
                    
                    if (s.SourceContext.IsValid) break; // end of closure
                    
                    if (s is Return) break; // Return is also an ExpressionStatement
                    
                    ExpressionStatement exprSt = s as ExpressionStatement;
                    if (exprSt != null && exprSt.Expression.NodeType == NodeType.Dup)
                    {
                        // dup of closure node
                        continue;
                    }

                    AssignmentStatement assign = s as AssignmentStatement;
                    if (assign == null) break;
                    
                    MemberBinding mb = assign.Target as MemberBinding;
                    
                    if (mb == null) break;
                    
                    if (mb.TargetObject == null ||
                        (mb.TargetObject.Type != closureType && mb.TargetObject.NodeType != NodeType.Pop)) break;
                }

                if (endOfAssignmentsToClosureFields - 1 < firstBlock.Statements.Count &&
                    endOfAssignmentsToClosureFields >= 1 &&
                    IsDup(firstBlock.Statements[endOfAssignmentsToClosureFields - 1]))
                {
                    endOfAssignmentsToClosureFields--; // last dup is not part of closure init
                }

                dupStackTracker = new StackDepthTracker(introducedClosureLocal);
                for (int i = indexForClosureCreationStatement + 1; i < endOfAssignmentsToClosureFields; i++)
                {
                    var stmt = firstBlock.Statements[i];
                    
                    if (stmt == null) continue;
                    
                    if (preambleBlock != null)
                    {
                        preambleBlock.Statements.Add(stmt);
                    }

                    if (stmt.NodeType != NodeType.Nop)
                    {
                        // don't add nop's to contract initializer
                        if (dupStackTracker.IsValid)
                        {
                            contractInitializer.Add(dupStackTracker.Visit(stmt));
                        }
                        else
                        {
                            contractInitializer.Add((Statement) stmt.Clone());
                        }
                    }

                    if (preambleBlock != null)
                    {
                        firstBlock.Statements[i] = null;
                        // need to null them out so search below can be done starting at beginning of m's body
                    }
                }

                currentIndex = endOfAssignmentsToClosureFields;
            }

            return currentIndex;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Copies the closure initialization into the contractinitializer and preambleBlock (if non-null)
        /// </summary>
        internal static int MovePastClosureInit(Method m, Block firstBlock, ContractNodes contractNodes,
            StatementList contractInitializer, Block preambleBlock, int currentIndex,
            ref StackDepthTracker dupStackTracker, out TypeNode closureType)
        {
            int indexForClosureCreationStatement = currentIndex;
            
            closureType = null;
            Local introducedClosureLocal = null;
            
            while (indexForClosureCreationStatement < firstBlock.Statements.Count)
            {
                var closureCreationCandidate = firstBlock.Statements[indexForClosureCreationStatement];
                closureType = IsClosureCreation(m, closureCreationCandidate);
                
                if (closureType != null) break;
                
                if (contractNodes.IsContractOrValidatorOrAbbreviatorCall(closureCreationCandidate))
                {
                    // found contracts before closure creation, so get out
                    return currentIndex;
                }

                // Following code was changed from simple check that closureCreationCandidate is not null and sourceContext is valid
                // to method call IsBaseConstructorCall.
                // Here is a history of this fix.
                // Original code led to NullReferenceException for constructor with capturing lambda 
                // and field-like initializer compiled with VS2015.
                //
                // With VS2015 order of initialization was changed. Consider constructor initialization:
                // VS2013:
                // 1. Field-like initialization
                // 2. Closure creation
                // 3. Base class constructor call
                // 4. Constructor body
                // VS2015
                // 1. Closure creation
                // 2. Field-like initialization
                // 3. Base class constructor call
                // 4. Constructor body 
                // 
                // Previous version of the code worked incorrectly with VS2015 compiler.
                // Because field-like initializer satisfy original criteria the function
                // was unable to find closure initializer properly.
                if (IsChainConstructorCall(closureCreationCandidate))
                {
                    return currentIndex;
                }
                
                indexForClosureCreationStatement++;
            }

            if (closureType != null && indexForClosureCreationStatement < firstBlock.Statements.Count)
            {
                // then there is a set of statements to add to the preamble block
                // up to and including "local := new ClosureClass();"
                for (int i = currentIndex; i <= indexForClosureCreationStatement; i++)
                {
                    if (firstBlock.Statements[i] == null) continue;

                    if (preambleBlock != null)
                    {
                        preambleBlock.Statements.Add(firstBlock.Statements[i]);
                    }

                    Local existingClosureLocal;
                    if (IsClosureCreation(m, firstBlock.Statements[i], out existingClosureLocal))
                    {
                        if (existingClosureLocal == null)
                        {
                            // introduce one
                            ExpressionStatement estmt = firstBlock.Statements[i] as ExpressionStatement;
                            if (estmt != null)
                            {
                                introducedClosureLocal = new Local(closureType);
                                contractInitializer.Add(
                                    new AssignmentStatement(introducedClosureLocal, (Expression) estmt.Expression.Clone()));
                            }
                            else
                            {
                                contractInitializer.Add((Statement) firstBlock.Statements[i].Clone());
                            }
                        }
                        else
                        {
                            contractInitializer.Add((Statement) firstBlock.Statements[i].Clone());
                        }
                    }
                    else
                    {
                        contractInitializer.Add((Statement) firstBlock.Statements[i].Clone());
                    }

                    if (preambleBlock != null)
                    {
                        firstBlock.Statements[i] = null;
                        // need to null them out so search below can be done starting at beginning of m's body
                    }
                }

                // Some number of assignment statements of the form "local.f := f;" where "f" is a parameter
                // that is captured by the closure.
                //
                // Roslyn generates code as follows:
                //   new Closure()
                //   dup
                //   ldarg f
                //   stfld f
                //   dup
                //   ldarg q
                //   stfld q
                //   dup...
                // 

                int endOfAssignmentsToClosureFields = indexForClosureCreationStatement + 1;
                for (; endOfAssignmentsToClosureFields < firstBlock.Statements.Count; endOfAssignmentsToClosureFields++)
                {
                    Statement s = firstBlock.Statements[endOfAssignmentsToClosureFields];
                    if (s == null) continue;

                    if (s.NodeType == NodeType.Nop) continue;
                    
                    if (s.SourceContext.IsValid) break; // end of closure
                    
                    if (s is Return) break; // Return is also an ExpressionStatement
                    
                    ExpressionStatement exprSt = s as ExpressionStatement;
                    if (exprSt != null && exprSt.Expression.NodeType == NodeType.Dup)
                    {
                        // dup of closure node
                        continue;
                    }

                    AssignmentStatement assign = s as AssignmentStatement;
                    if (assign == null) break;
                    
                    MemberBinding mb = assign.Target as MemberBinding;
                    
                    if (mb == null) break;
                    
                    if (mb.TargetObject == null ||
                        (mb.TargetObject.Type != closureType && mb.TargetObject.NodeType != NodeType.Pop)) break;
                }

                if (endOfAssignmentsToClosureFields - 1 < firstBlock.Statements.Count &&
                    endOfAssignmentsToClosureFields >= 1 &&
                    IsDup(firstBlock.Statements[endOfAssignmentsToClosureFields - 1]))
                {
                    endOfAssignmentsToClosureFields--; // last dup is not part of closure init
                }

                dupStackTracker = new StackDepthTracker(introducedClosureLocal);
                for (int i = indexForClosureCreationStatement + 1; i < endOfAssignmentsToClosureFields; i++)
                {
                    var stmt = firstBlock.Statements[i];
                    
                    if (stmt == null) continue;
                    
                    if (preambleBlock != null)
                    {
                        preambleBlock.Statements.Add(stmt);
                    }

                    if (stmt.NodeType != NodeType.Nop)
                    {
                        // don't add nop's to contract initializer
                        if (dupStackTracker.IsValid)
                        {
                            contractInitializer.Add(dupStackTracker.Visit(stmt));
                        }
                        else
                        {
                            contractInitializer.Add((Statement) stmt.Clone());
                        }
                    }

                    if (preambleBlock != null)
                    {
                        firstBlock.Statements[i] = null;
                        // need to null them out so search below can be done starting at beginning of m's body
                    }
                }

                currentIndex = endOfAssignmentsToClosureFields;
            }

            return currentIndex;
        }
Ejemplo n.º 3
0
        private static int ContractStartInMoveNext(ContractNodes contractNodes, Method moveNext, out int statementIndex,
            Method origMethod)
        {
            Contract.Requires(contractNodes != null);
            Contract.Requires(moveNext != null);
            Contract.Requires(moveNext.Body != null);
            Contract.Requires(moveNext.Body.Statements != null);
            Contract.Ensures(Contract.ValueAtReturn(out statementIndex) >= 0);
            Contract.Ensures(Contract.Result<int>() >= -1);
            Contract.Ensures(Contract.Result<int>() < moveNext.Body.Statements.Count);

            Block body = moveNext.Body;
            bool isAsync = moveNext.IsAsync;
            statementIndex = 0;
            var blocks = body.Statements;

            bool stateIsRead = false;

            var fieldScanner = new HelperMethods.ExpressionScanner((field, isStore) =>
            {
                if (isStore) return;
                if (field != null && field.Name != null && field.Name.Name.Contains("<>") &&
                    field.Name.Name.Contains("__state"))
                {
                    stateIsRead = true;
                }
            });

            fieldScanner.Visit(body.Statements);

            // Dictionary tracks local variable values and a bit saying if the value is the state (in that case it is 0).
            Dictionary<Variable, Pair<int, EvalKind>> env = new Dictionary<Variable, Pair<int, EvalKind>>();
            int currentBlockIndex = 0;
            bool seenFinalCompare = false;
            bool lastBranchNonConditional = false;

            while (currentBlockIndex >= 0 && currentBlockIndex < blocks.Count)
            {
                var block = blocks[currentBlockIndex] as Block;
                if (block == null || block.Statements == null) continue;

                for (int i = 0; i < block.Statements.Count; i++)
                {
                    var stmt = block.Statements[i];
                    if (stmt == null) continue;

                    if (contractNodes.IsContractOrValidatorOrAbbreviatorCall(stmt))
                    {
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    Branch branch = stmt as Branch;
                    if (branch != null)
                    {
                        if (branch.Condition == null)
                        {
                            currentBlockIndex = FindTargetBlock(blocks, branch.Target, currentBlockIndex);
                            lastBranchNonConditional = true;
                            goto OuterLoop;
                        }

                        var value = EvaluateExpression(branch.Condition, env, seenFinalCompare, isAsync);
                        if (value.Two == EvalKind.IsDisposingTest)
                        {
                            if (value.One != 0)
                            {
                                return FindTargetBlock(blocks, branch.Target, currentBlockIndex);
                            }
                            
                            if (i + 1 < block.Statements.Count)
                            {
                                statementIndex = i + 1;
                                return currentBlockIndex;
                            }
                            
                            if (currentBlockIndex + 1 < body.Statements.Count)
                            {
                                return currentBlockIndex + 1;
                            }

                            return -1;
                        }

                        if (seenFinalCompare)
                        {
                            // must be the end.
                            statementIndex = i;
                            return currentBlockIndex;
                        }

                        seenFinalCompare = (value.Two == EvalKind.IsFinalCompare || value.Two == EvalKind.IsStateValue);

                        if (value.One != 0)
                        {
                            currentBlockIndex = FindTargetBlock(blocks, branch.Target, currentBlockIndex);
                            goto OuterLoop;
                        }
                        
                        continue;
                    }

                    if (isAsync && lastBranchNonConditional)
                    {
                        // not a branch and last one was non-conditional
                        // must be the end.
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    var swtch = stmt as SwitchInstruction;
                    if (swtch != null)
                    {
                        if (seenFinalCompare)
                        {
                            // must be the end.
                            statementIndex = i;
                            return currentBlockIndex;
                        }

                        var value = EvaluateExpression(swtch.Expression, env, seenFinalCompare, isAsync);
                        if (value.One < 0 || swtch.Targets == null || value.One >= swtch.Targets.Count)
                        {
                            // fall through
                            if (isAsync)
                            {
                                seenFinalCompare = true;
                            }

                            continue;
                        }

                        currentBlockIndex = FindTargetBlock(blocks, swtch.Targets[value.One], currentBlockIndex);
                        // assume seen final compare
                        seenFinalCompare = true;
                        goto OuterLoop;
                    }

                    if (HelperMethods.IsClosureCreation(origMethod, stmt) != null)
                    {
                        // end of trace
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    AssignmentStatement assign = stmt as AssignmentStatement;
                    if (assign != null)
                    {
                        if (assign.Source is ConstructArray)
                        {
                            // treat as beginning of contract (typically a params array)
                            statementIndex = i;
                            return currentBlockIndex;
                        }

                        var value = EvaluateExpression(assign.Source, env, seenFinalCompare, isAsync);
                        if (IsThisDotState(assign.Target))
                        {
                            // end of trace
                            if (i + 1 < block.Statements.Count)
                            {
                                statementIndex = i + 1;
                                return currentBlockIndex;
                            }

                            if (currentBlockIndex + 1 < body.Statements.Count)
                            {
                                return currentBlockIndex + 1;
                            }

                            return -1;
                        }

                        if (IsDoFinallyBodies(assign.Target) && !stateIsRead)
                        {
                            // end of trace
                            if (i + 1 < block.Statements.Count)
                            {
                                statementIndex = i + 1;
                                return currentBlockIndex;
                            }

                            if (currentBlockIndex + 1 < body.Statements.Count)
                            {
                                return currentBlockIndex + 1;
                            }

                            return -1;
                        }

                        var target = assign.Target as Variable;
                        if (target != null) env[target] = value;
                        if (seenFinalCompare && !(value.Two == EvalKind.IsDisposingTest))
                        {
                            // must be the end.
                            statementIndex = i;
                            return currentBlockIndex;
                        }

                        continue;
                    }

                    if (seenFinalCompare)
                    {
                        // must be the end.
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    switch (stmt.NodeType)
                    {
                        case NodeType.Nop:
                        case NodeType.ExpressionStatement:
                            // skip: C# compiler emits funky pushes then pops
                            break;

                        default:
                            Contract.Assume(false, string.Format("Unexpected node type '{0}'", stmt.NodeType));
                            return -1;
                    }
                }

                // next block in body
                currentBlockIndex++;
                OuterLoop:
                ;
            }

            return -1;
        }
Ejemplo n.º 4
0
        private static int ContractStartInMoveNext(ContractNodes contractNodes, Method moveNext, out int statementIndex,
            Method origMethod)
        {
            Contract.Requires(contractNodes != null);
            Contract.Requires(moveNext != null);
            Contract.Requires(moveNext.Body != null);
            Contract.Requires(moveNext.Body.Statements != null);
            Contract.Ensures(Contract.ValueAtReturn(out statementIndex) >= 0);
            Contract.Ensures(Contract.Result<int>() >= -1);
            Contract.Ensures(Contract.Result<int>() < moveNext.Body.Statements.Count);

            Block body = moveNext.Body;
            bool isAsync = moveNext.IsAsync;
            statementIndex = 0;
            var blocks = body.Statements;

            bool stateIsRead = false;

            var fieldScanner = new HelperMethods.ExpressionScanner((field, isStore) =>
            {
                if (isStore) return;
                if (field != null && field.Name != null && field.Name.Name.Contains("<>") &&
                    field.Name.Name.Contains("__state"))
                {
                    stateIsRead = true;
                }
            });

            fieldScanner.Visit(body.Statements);

            // Dictionary tracks local variable values and a bit saying if the value is the state (in that case it is 0).
            Dictionary<Variable, Pair<int, EvalKind>> env = new Dictionary<Variable, Pair<int, EvalKind>>();
            int currentBlockIndex = 0;
            bool seenFinalCompare = false;
            bool lastBranchNonConditional = false;

            while (currentBlockIndex >= 0 && currentBlockIndex < blocks.Count)
            {
                var block = blocks[currentBlockIndex] as Block;
                if (block == null || block.Statements == null) continue;

                for (int i = 0; i < block.Statements.Count; i++)
                {
                    var stmt = block.Statements[i];
                    if (stmt == null) continue;

                    if (contractNodes.IsContractOrValidatorOrAbbreviatorCall(stmt))
                    {
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    Branch branch = stmt as Branch;
                    if (branch != null)
                    {
                        if (branch.Condition == null)
                        {
                            currentBlockIndex = FindTargetBlock(blocks, branch.Target, currentBlockIndex);
                            lastBranchNonConditional = true;
                            goto OuterLoop;
                        }

                        // Roslyn-based compiler introduced new pattern for async methods with 2 await statements.
                        // Current implementation sets seenFinalCompare to true when `if (num != 0)` code was found in the MoveNext method.
                        // This usually meant that async preamble is finished and next statement could be a contract statement
                        // (or legacy contract statement, doesn't matter).
                        // But VS2015 compiler changes the behavior and right after `if (num != 0)` there another check that should be skipped.
                        // Please see additional comments at IsRoslynStateCheckForSecondFinishedAwaiter
                        if (seenFinalCompare && isAsync && IsRoslynStateCheckForSecondFinishedAwaiter(branch.Condition, env, ignoreUnknown: true))
                        {
                            // just skipping current statement!
                            continue;
                        }

                        var value = EvaluateExpression(branch.Condition, env, seenFinalCompare, isAsync);
                        if (value.Two == EvalKind.IsDisposingTest)
                        {
                            if (value.One != 0)
                            {
                                return FindTargetBlock(blocks, branch.Target, currentBlockIndex);
                            }

                            if (i + 1 < block.Statements.Count)
                            {
                                statementIndex = i + 1;
                                return currentBlockIndex;
                            }

                            if (currentBlockIndex + 1 < body.Statements.Count)
                            {
                                return currentBlockIndex + 1;
                            }

                            return -1;
                        }

                        if (seenFinalCompare)
                        {
                            // must be the end.
                            statementIndex = i;
                            return currentBlockIndex;
                        }

                        seenFinalCompare = (value.Two == EvalKind.IsFinalCompare || value.Two == EvalKind.IsStateValue);

                        if (value.One != 0)
                        {
                            currentBlockIndex = FindTargetBlock(blocks, branch.Target, currentBlockIndex);
                            goto OuterLoop;
                        }

                        continue;
                    }

                    if (isAsync && lastBranchNonConditional)
                    {
                        // not a branch and last one was non-conditional
                        // must be the end.
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    var swtch = stmt as SwitchInstruction;
                    if (swtch != null)
                    {
                        if (seenFinalCompare)
                        {
                            // must be the end.
                            statementIndex = i;
                            return currentBlockIndex;
                        }

                        var value = EvaluateExpression(swtch.Expression, env, seenFinalCompare, isAsync);
                        if (value.One < 0 || swtch.Targets == null || value.One >= swtch.Targets.Count)
                        {
                            // fall through
                            if (isAsync)
                            {
                                seenFinalCompare = true;
                            }

                            continue;
                        }

                        currentBlockIndex = FindTargetBlock(blocks, swtch.Targets[value.One], currentBlockIndex);
                        // assume seen final compare
                        seenFinalCompare = true;
                        goto OuterLoop;
                    }

                    if (HelperMethods.IsClosureCreation(origMethod, stmt) != null)
                    {
                        // end of trace
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    AssignmentStatement assign = stmt as AssignmentStatement;
                    if (assign != null)
                    {
                        if (assign.Source is ConstructArray)
                        {
                            // treat as beginning of contract (typically a params array)
                            statementIndex = i;
                            return currentBlockIndex;
                        }
                        
                        // Roslyn-based compiler will generate AssignmentStatement with
                        // Pop as a source node (any complex if-statement will do this). 
                        // This type of node will lead to
                        // InvalidOperationException in the EvaluateExpression method.
                        // This if statement avoids the failure and fixes #172.
                        if (assign.Source.NodeType == NodeType.Pop)
                            continue;

                        var value = EvaluateExpression(assign.Source, env, seenFinalCompare, isAsync);
                        if (IsThisDotState(assign.Target))
                        {
                            // end of trace
                            if (i + 1 < block.Statements.Count)
                            {
                                statementIndex = i + 1;
                                return currentBlockIndex;
                            }

                            if (currentBlockIndex + 1 < body.Statements.Count)
                            {
                                return currentBlockIndex + 1;
                            }

                            return -1;
                        }

                        if (IsDoFinallyBodies(assign.Target) && !stateIsRead)
                        {
                            // end of trace
                            if (i + 1 < block.Statements.Count)
                            {
                                statementIndex = i + 1;
                                return currentBlockIndex;
                            }

                            if (currentBlockIndex + 1 < body.Statements.Count)
                            {
                                return currentBlockIndex + 1;
                            }

                            return -1;
                        }

                        var target = assign.Target as Variable;
                        if (target != null) env[target] = value;
                        if (seenFinalCompare && !(value.Two == EvalKind.IsDisposingTest))
                        {
                            // must be the end.
                            statementIndex = i;
                            return currentBlockIndex;
                        }

                        continue;
                    }

                    if (seenFinalCompare)
                    {
                        // must be the end.
                        statementIndex = i;
                        return currentBlockIndex;
                    }

                    switch (stmt.NodeType)
                    {
                        case NodeType.Nop:
                        case NodeType.ExpressionStatement:
                            // skip: C# compiler emits funky pushes then pops
                            break;

                        default:
                            Contract.Assume(false, string.Format("Unexpected node type '{0}'", stmt.NodeType));
                            return -1;
                    }
                }

                // next block in body
                currentBlockIndex++;
                OuterLoop:
                ;
            }

            return -1;
        }