예제 #1
0
        internal static Local ExtractPreamble(Method m, ContractNodes contractNodes, Block contractInitializer,
            out Block postPreamble, ref StackDepthTracker dupStackTracker, bool isVB)
        {
            postPreamble = null;

            if (m == null || m.Body == null || m.Body.Statements == null || !(m.Body.Statements.Count > 0)) return null;
            
            Block firstBlock = m.Body.Statements[0] as Block;
            if (firstBlock == null) return null;
            
            Block preambleBlock = new PreambleBlock(new StatementList(0));

            Local result;
            
            int nextInstructionInFirstBlock = ExtractClosureInitializationAndLocalThis(m, firstBlock, contractNodes,
                contractInitializer, preambleBlock, 0, out result, ref dupStackTracker);

            // Type (2): Base ctor or deferring ctor call

            if (m is InstanceInitializer)
            {
                int i, j;
                bool found;

                // Type (2a): Assignments to fields of class that had initializers

                // Just extract the statements for (2a) as part of what gets extracted for (2b)

                // Type (2b): base or deferring ctor call

                found = SearchClump(m.Body.Statements, 
                    delegate(Statement s)
                    {
                        if (s == null) return false;

                        ExpressionStatement es = s as ExpressionStatement;
                        if (es == null) return false;
                    
                        MethodCall mc = es.Expression as MethodCall;
                        if (mc == null) return false;
                    
                        MemberBinding mb = mc.Callee as MemberBinding;
                        if (mb == null) return false;
                    
                        Method callee = mb.BoundMember as Method;
                    
                        // can't depend on the TargetObject being "this": it could be "pop" if
                        // the arguments to the call had any control flow
                        // e.g., a boolean short-circuit expression or a ternary expression.
                        //if (mb.TargetObject is This && callee is InstanceInitializer) return true;
                        if (callee is InstanceInitializer
                            &&
                            ((callee.DeclaringType == m.DeclaringType || callee.DeclaringType.Template == m.DeclaringType)
                             // deferring ctor call
                             ||
                             (callee.DeclaringType == m.DeclaringType.BaseType ||
                              // base ctor call
                              callee.DeclaringType.Template == m.DeclaringType.BaseType.Template)))
                        {
                            return true;
                        }

                        return false;

                    }, 
                    out i, out j);

                if (found)
                {
                    // for VB constructors and C# constructors with closures, we need to extend the scope to include field initializers

                    Block b = (Block) m.Body.Statements[i];
                    int k = j + 1;
                    Local extraClosureLocal = null;
                    int stackDepth = 0;

                    for (; k < b.Statements.Count; k++)
                    {
                        // skip over auxiliary closure creation
                        if (extraClosureLocal == null && IsClosureCreation(m, b.Statements[k], out extraClosureLocal))
                        {
                            continue;
                        }

                        switch (b.Statements[k].NodeType)
                        {
                            case NodeType.Nop:
                                continue;

                            case NodeType.AssignmentStatement:
                            {
                                AssignmentStatement assgmt = (AssignmentStatement) b.Statements[k];
                                MemberBinding mb = assgmt.Target as MemberBinding;
                                if (mb == null)
                                {
                                    // we might be initializing locals for default values etc
                                    if (stackDepth > 0) continue;
                                    
                                    goto doneWithFieldInits;
                                }

                                if (!(mb.BoundMember is Field))
                                {
                                    // we might be initializing locals for default values etc
                                    if (stackDepth > 0) continue;
                                    goto doneWithFieldInits;
                                }

                                if (mb.TargetObject == null)
                                {
                                    // we might be initializing locals for default values etc
                                    if (stackDepth > 0) continue;
                                    goto doneWithFieldInits;
                                }

                                // there are 2 cases here. Either we are assigning to this.f = ..., which is a field initialization,
                                // or it is initializing the closure field for "this" with this.
                                if (mb.TargetObject.NodeType == NodeType.This)
                                {
                                    // okay field initialization
                                    continue;
                                }

                                if (mb.TargetObject.NodeType == NodeType.Pop)
                                {
                                    // okay field initialization (popping this)
                                    stackDepth--;
                                    continue;
                                }

                                // we might also be initializing the extra closure fields, if the target is the extraClosureLocal.
                                if (mb.TargetObject == extraClosureLocal)
                                {
                                    continue;
                                }

                                if (mb.TargetObject.NodeType == NodeType.Local && assgmt.Source != null &&
                                    assgmt.Source.NodeType == NodeType.This && mb.BoundMember.Name != null &&
                                    (mb.BoundMember.Name.Name.EndsWith("_this") || mb.BoundMember.Name.Name == "$VB$Me"))
                                {
                                    // closure initialization (storing this)
                                    // add it to postPreamble, since it needs to be inserted for duplicate closure object
                                    postPreamble = new Block(new StatementList(1));
                                    postPreamble.Statements.Add((Statement) b.Statements[k].Clone());
                                    continue;
                                }

                                goto doneWithFieldInits;
                            }

                            case NodeType.ExpressionStatement:
                            {
                                ExpressionStatement estmt = (ExpressionStatement) b.Statements[k];
                                if (estmt.Expression != null && estmt.Expression is This)
                                {
                                    // handle special push/pop pattern occurring occasionally
                                    stackDepth++;
                                    continue;
                                }

                                // handle ctor calls on addresses of value types
                                // NOTE: this could be mistakenly the first user statement.
                                MethodCall mc = estmt.Expression as MethodCall;
                                if (mc == null) goto doneWithFieldInits;

                                MemberBinding mb = mc.Callee as MemberBinding;
                                if (mb == null) goto doneWithFieldInits;
                                
                                if (!(mb.BoundMember is InstanceInitializer)) goto doneWithFieldInits;
                                
                                if (!mb.BoundMember.DeclaringType.IsValueType) goto doneWithFieldInits;
                                
                                if (mb.TargetObject.NodeType != NodeType.AddressOf) goto doneWithFieldInits;
                                
                                continue;
                            }

                            default:
                                goto doneWithFieldInits;
                        }
                    }

                    doneWithFieldInits:

                    StatementList sl2 = ExtractClump(m.Body.Statements, 0, 0, i, k - 1);
                    preambleBlock.Statements.Add(new Block(sl2));

                    ExtractVB_ENCCallToPreamble(m.Body.Statements[i] as Block, k, preambleBlock.Statements);
                }
                else
                {
                    // for struct ctors, we may not have a "this" call

                    // for C# constructors with closures, we need to extend the scope

                    Block b = firstBlock;
                    int k = nextInstructionInFirstBlock;
                    Local extraClosureLocal = null;

                    for (; k < b.Statements.Count; k++)
                    {
                        // skip over auxiliary closure creation and initialization
                        if (extraClosureLocal == null && IsClosureCreation(m, b.Statements[k], out extraClosureLocal))
                        {
                            continue;
                        }

                        switch (b.Statements[k].NodeType)
                        {
                            case NodeType.Nop:
                                continue;

                            case NodeType.AssignmentStatement:
                            {
                                AssignmentStatement assgmt = (AssignmentStatement) b.Statements[k];
                                MemberBinding mb = assgmt.Target as MemberBinding;
                                
                                if (mb == null) goto doneWithFieldInits;

                                if (!(mb.BoundMember is Field)) goto doneWithFieldInits;
                                
                                if (mb.TargetObject == null) goto doneWithFieldInits;
                                
                                // we might be initializing the extra closure fields, if the target is the extraClosureLocal.
                                if (mb.TargetObject == extraClosureLocal)
                                {
                                    continue;
                                }

                                goto doneWithFieldInits;
                            }

                            default:
                                goto doneWithFieldInits;
                        }
                    }

                    doneWithFieldInits:
                    
                    for (int toCopy = nextInstructionInFirstBlock; toCopy < k; toCopy++)
                    {
                        preambleBlock.Statements.Add(firstBlock.Statements[toCopy]);
                        firstBlock.Statements[toCopy] = null;
                    }
                }
            }

            // Create a new body, add the preamble block, then all of the other blocks

            StatementList sl = new StatementList(m.Body.Statements.Count);
            sl.Add(preambleBlock);

            if (m.Body != null)
            {
                foreach (Block b in m.Body.Statements)
                {
                    if (b != null) sl.Add(b);
                }
            }

            m.Body.Statements = sl;

            return result;
        }
예제 #2
0
        /// <summary>
        /// Extract prefix code prior to contracts such as closure initialization and locals
        /// </summary>
        internal static int ExtractClosureInitializationAndLocalThis(
            Method m,
            Block firstBlock,
            ContractNodes contractNodes,
            Block contractInitializer,
            Block preambleBlock,
            int currentIndex,
            out Local localAliasingThis,
            ref StackDepthTracker dupStackTracker)
        {
            localAliasingThis = null;

            // Any prefix of null statements and nops should go into the preamble block

            //      In addition, for VB structure ctors, they start with initobj this
            //      ------------------------------------------------------------------------
            Contract.Assert(currentIndex <= firstBlock.Statements.Count);

            while (currentIndex < firstBlock.Statements.Count)
            {
                Statement s = firstBlock.Statements[currentIndex];

                if (s != null && s.NodeType != NodeType.Nop)
                {
                    // check for VB "initobj this"
                    if (!m.DeclaringType.IsValueType) break;

                    if (!(m is InstanceInitializer)) break;

                    AssignmentStatement initObj = s as AssignmentStatement;
                    if (initObj == null) break;
                    
                    AddressDereference addrderef = initObj.Target as AddressDereference;
                    if (addrderef == null) break;
                    
                    if (addrderef.Address != m.ThisParameter) break;
                    
                    Literal initObjArg = initObj.Source as Literal;
                    if (initObjArg == null) break;
                    
                    if (initObjArg.Value != null) break;
                }
                
                preambleBlock.Statements.Add(s);

                var oldCount = firstBlock.Statements.Count;
                var oldStats = firstBlock.Statements;
                
                firstBlock.Statements[currentIndex] = null;

                Contract.Assert(oldStats == firstBlock.Statements);
                Contract.Assert(oldCount == firstBlock.Statements.Count);
                
                currentIndex++;
                
                Contract.Assert(currentIndex <= firstBlock.Statements.Count);
            }

            Contract.Assert(currentIndex <= firstBlock.Statements.Count);

            // Type (1): Closure creation and initialization

            //      ---------------------------------------------
            TypeNode closureType;
            currentIndex = MovePastClosureInit(m, firstBlock, contractNodes, contractInitializer.Statements,
                preambleBlock, currentIndex, ref dupStackTracker, out closureType);

            // Local @this in contract classes

            //      -------------------------------
            // For contract classes, since we require that they use explicit interface implementation,
            // allow the single assignment statement "J this_j = this;" as part of the prelude so that
            // explicit casts are not needed all over the contracts.
            Interface iface = HelperMethods.IsContractTypeForSomeOtherType(m.DeclaringType, contractNodes) as Interface;
            if (iface != null && currentIndex < firstBlock.Statements.Count)
            {
                AssignmentStatement assgn = firstBlock.Statements[currentIndex] as AssignmentStatement;
                if (assgn != null)
                {
                    if (assgn.Target is Local && assgn.Target.Type == iface && assgn.Source is This)
                    {
                        // we currently keep the local initializer in both "contractLocalsInitializer" for static decoding
                        // AND in the preamble block for runtime checking code generation.
                        localAliasingThis = (Local) assgn.Target;

                        //        contractInitializer.Statements.Add((Statement)(firstBlock.Statements[currentIndex].Clone()));

                        //        preambleBlock.Statements.Add(firstBlock.Statements[currentIndex]);
                        firstBlock.Statements[currentIndex] = null;

                        currentIndex++;
                    }
                }
            }

            // VB hoists some other literal assignments to the start of the method.

            while (currentIndex < firstBlock.Statements.Count)
            {
                Statement s = firstBlock.Statements[currentIndex];
                if (s != null && s.NodeType != NodeType.Nop)
                {
                    // check for VB literal assignments
                    if (!s.SourceContext.Hidden) break;

                    AssignmentStatement litAssgn = s as AssignmentStatement;
                    
                    if (litAssgn == null) break;
                    var local = litAssgn.Target as Local;
                    
                    if (local == null) break;
                    
                    if (local.Name == null) break;
                    
                    if (!local.Name.Name.StartsWith("VB$t_ref$L")) break;
                    // okay, we are assigning one of these locals. Put it into the preamble.
                }

                preambleBlock.Statements.Add(s);
                
                var oldCount = firstBlock.Statements.Count;
                var oldStats = firstBlock.Statements;
                
                firstBlock.Statements[currentIndex] = null;
                
                Contract.Assert(oldStats == firstBlock.Statements);
                Contract.Assert(oldCount == firstBlock.Statements.Count);
                
                currentIndex++;
                
                Contract.Assert(currentIndex <= firstBlock.Statements.Count);
            }
            
            Contract.Assert(currentIndex <= firstBlock.Statements.Count);

            return currentIndex;
        }
예제 #3
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;
        }
예제 #4
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;
        }