Ejemplo n.º 1
0
    public override Expression VisitOldExpression(OldExpression oldExpression) {
      if (this.topLevelClosureClass != null) {
        #region In Closure ==> Create a field
        // Since we're within a closure, we can't create a local to hold the value of the old expression
        // but instead have to create a field for it. That field can be a member of the top-level
        // closure class since nothing mentioned in the old expression (except possibly for the
        // bound variables of enclosing quantifications) should be anything captured from
        // an inner anonymous delegate.

        // BUT, first we have to know if the old expression depends on any of the bound
        // variables of the closures in which it is located. If not, then we can implement
        // it as a scalar and just generate the assignment "closure_class.field := e" for
        // "Old(e)" to take a snapshot of e's value in the prestate. If it does depend on
        // any of the bound variables, then we need to generate a set of for-loops that
        // compute the indices and values of e for each tuple of indices so it can be retrieved
        // (given the indices) in the post-state.
        CollectBoundVariables cbv = new CollectBoundVariables(this.stackOfBoundVariables);
        cbv.VisitExpression(oldExpression.expression);

        SubstituteClosureClassWithinOldExpressions subst =
            new SubstituteClosureClassWithinOldExpressions(this.closureLocals);
        Expression e = subst.VisitExpression(oldExpression.expression);
        if (cbv.FoundVariables.Count == 0) {
          #region Use a scalar for the old variable
          Local closureLocal;
          if (!this.closureLocals.TryGetValue(this.topLevelClosureClass, out closureLocal))
          {
            Contract.Assume(false, "can't find closure local!");
          }
          #region Define a scalar
          var clTemplate = HelperMethods.Unspecialize(this.topLevelClosureClass);
          Field f = new Field(clTemplate,
            null,
            FieldFlags.CompilerControlled | FieldFlags.Public,
            Identifier.For("_old" + oldExpression.expression.UniqueKey.ToString()), // unique name for this old expr.
            oldExpression.Type,
            null);
          clTemplate.Members.Add(f);
          // now produce properly instantiated field
          f = (Field)Rewriter.GetMemberInstanceReference(f, this.topLevelClosureClass);
          #endregion
          #region Generate code to store value in prestate
          this.prestateValuesOfOldExpressions.Statements.Add(new AssignmentStatement(new MemberBinding(closureLocal, f), e));
          #endregion
          #region Return expression to be used in poststate
          // Return an expression that will evaluate in the poststate to the value of the old
          // expression in the prestate. This will be this.up.f where "up" is the field C#
          // generated to point to the instance of the top-level closure class.
          if (this.PointerToTopLevelClosureClass == null) {
            // then the old expression occurs in the top-level closure class. Just return "this.f"
            // where "this" refers to the top-level closure class.
            return new MemberBinding(new This(this.currentClosureClass), f);
          } else {
            return new MemberBinding(
              new MemberBinding(new This(this.currentClosureClass), this.PointerToTopLevelClosureClass),
              f);
          }
          #endregion
          #endregion
        } else {
          // the Old expression *does* depend upon at least one of the bound variable
          // in a ForAll or Exists expression
          #region Use an indexed variable for the old variable
          TypeNode oldVariableTypeDomain;
          #region Decide if domain is one-dimensional or not
          bool oneDimensional = cbv.FoundVariables.Count == 1 && cbv.FoundVariables[0].Type.IsValueType;
          if (oneDimensional) {
            // a one-dimensional old-expression can use the index variable directly
            oldVariableTypeDomain = cbv.FoundVariables[0].Type;
          } else {
            oldVariableTypeDomain = SystemTypes.GenericList.GetTemplateInstance(this.module, SystemTypes.Int32);
          }
          #endregion
          TypeNode oldVariableTypeRange = oldExpression.Type;
          TypeNode oldVariableType = SystemTypes.GenericDictionary.GetTemplateInstance(this.module, oldVariableTypeDomain, oldVariableTypeRange);
          Local closureLocal;
          if (!this.closureLocals.TryGetValue(this.topLevelClosureClass, out closureLocal))
          {
            Contract.Assume(false, "can't find closure local");
          }
          #region Define an indexed variable
          var clTemplate = HelperMethods.Unspecialize(this.topLevelClosureClass);
          Field f = new Field(clTemplate,
            null,
            FieldFlags.CompilerControlled | FieldFlags.Assembly, // can't be private or protected because it needs to be accessed from inner (closure) classes that don't inherit from the class this field is added to.
            Identifier.For("_old" + oldExpression.expression.UniqueKey.ToString()), // unique name for this old expr.
            oldVariableType,
            null);
          clTemplate.Members.Add(f);
          // instantiate f
          f = (Field)Rewriter.GetMemberInstanceReference(f, closureLocal.Type);
          #endregion
          #region Generate code to initialize the indexed variable
          Statement init = new AssignmentStatement(
            new MemberBinding(closureLocal, f),
            new Construct(new MemberBinding(null, oldVariableType.GetConstructor()), null));
          this.prestateValuesOfOldExpressions.Statements.Add(init);
          #endregion
          #region Generate code to store values in prestate
          #region Create assignment: this.closure.f[i,j,k,...] = e;
          Method setItem = oldVariableType.GetMethod(Identifier.For("set_Item"), oldVariableTypeDomain, oldVariableTypeRange);
          Expression index;
          if (oneDimensional) {
            index = cbv.FoundVariables[0];
          } else {
            //InstanceInitializer ctor =
            //  ContractNodes.TupleClass.GetConstructor(SystemTypes.Int32.GetArrayType(1));
            //Expression index = new Construct(new MemberBinding(null,ctor),new ExpressionList(
            index = Literal.Null;
          }
          MethodCall mc = new MethodCall(new MemberBinding(new MemberBinding(closureLocal, f), setItem),
            new ExpressionList(index, e));
          Statement stat = new ExpressionStatement(mc);
          #endregion
          List<Local> locals = new List<Local>(this.stackOfBoundVariables.Count);
          TrivialHashtable paramMap = new TrivialHashtable();
          #region Generate a local for each bound variable to use in for-loop
          foreach (Variable v in this.stackOfBoundVariables) {
            Local l = new Local(Identifier.Empty, v.Type);
            paramMap[v.UniqueKey] = l;
            locals.Add(l);
          }
          #endregion
          #region Substitute locals for bound variables in old expression *AND* in inner loop bounds
          SubstituteParameters sps = new SubstituteParameters(paramMap, this.stackOfBoundVariables);
          sps.Visit(stat);
          #endregion
          #region Create nested for-loops around assignment
          // keep track of when the first variable is used (from innermost to outermost)
          // as soon as the first one is needed because the old expression depends on it,
          // then keep all enclosing loops. It would be possible to keep only those where
          // the necessary loops have loop bounds that depend on an enclosing loop, but I
          // haven't calculated that, so just keep them all. For instance, if the old expression
          // depends on j and the loops are "for i,0,n" and inside that "for j,0,i", then need
          // both loops. If the inner loop bounds were 0 and n, then wouldn't need the outer
          // loop.
          bool usedAVariable = false;
          for (int i = this.stackOfBoundVariables.Count - 1; 0 <= i; i--) {
            if (!usedAVariable
              && !cbv.FoundVariables.Contains(this.stackOfBoundVariables[i])) continue;
            usedAVariable = true;
            Expression lowerBound = new Duplicator(this.module, this.currentClosureClass).VisitExpression(
              this.stackOfMethods[i].Operands[0]);
            lowerBound = subst.VisitExpression(lowerBound);
            lowerBound = sps.VisitExpression(lowerBound);
            Expression upperBound = new Duplicator(this.module, this.currentClosureClass).VisitExpression(
              this.stackOfMethods[i].Operands[1]);
            upperBound = subst.VisitExpression(upperBound);
            upperBound = sps.VisitExpression(upperBound);
            stat = RewriteHelper.GenerateForLoop(locals[i], lowerBound, upperBound, stat);
          }
          #endregion
          this.prestateValuesOfOldExpressions.Statements.Add(stat);
          #endregion
          #region Return expression to be used in poststate
          Method getItem = oldVariableType.GetMethod(Identifier.For("get_Item"), oldVariableTypeDomain);
          if (oneDimensional) {
            index = cbv.FoundReferences[0];
          } else {
            //InstanceInitializer ctor =
            //  ContractNodes.TupleClass.GetConstructor(SystemTypes.Int32.GetArrayType(1));
            //Expression index = new Construct(new MemberBinding(null,ctor),new ExpressionList(
            index = Literal.Null;
          }
          // Return an expression that will evaluate in the poststate to the value of the old
          // expression in the prestate. This will be this.up.f[i,j,k,...] where "up" is the field C#
          // generated to point to the instance of the top-level closure class.
          MemberBinding thisDotF;
          if (this.PointerToTopLevelClosureClass == null) {
            // then the old expression occurs in the top-level closure class. Just return "this.f"
            // where "this" refers to the top-level closure class.
            Contract.Assume(f != null);
            thisDotF = new MemberBinding(new This(clTemplate), HelperMethods.Unspecialize(f));
          } else {
            thisDotF = new MemberBinding(
              new MemberBinding(new This(clTemplate), this.PointerToTopLevelClosureClass),
              f);
          }
          return new MethodCall(new MemberBinding(thisDotF, getItem), new ExpressionList(index));
          #endregion
          #endregion
        }
        #endregion
      } else {
        #region Not in closure ==> Create a local variable
        Local l = GetLocalForOldExpression(oldExpression);
        #region Make sure local can be seen in the debugger (for the entire method, unfortunately)
        if (currentMethod.LocalList == null) {
          currentMethod.LocalList = new LocalList();
        }
        currentMethod.LocalList.Add(l);
        currentMethod.Body.HasLocals = true;
        #endregion
        this.prestateValuesOfOldExpressions.Statements.Add(
          new AssignmentStatement(l, oldExpression.expression));

        // Return an expression that will evaluate in the poststate to the value of the old
        // expression in the prestate. When we're not in a closure, this is just the local
        // itself.
        return l;
        #endregion
      }
    }
        public override Expression VisitOldExpression(OldExpression oldExpression)
        {
            if (this.topLevelClosureClass != null)
            {
                // In Closure ==> Create a field

                // Since we're within a closure, we can't create a local to hold the value of the old expression
                // but instead have to create a field for it. That field can be a member of the top-level
                // closure class since nothing mentioned in the old expression (except possibly for the
                // bound variables of enclosing quantifications) should be anything captured from
                // an inner anonymous delegate.

                // BUT, first we have to know if the old expression depends on any of the bound
                // variables of the closures in which it is located. If not, then we can implement
                // it as a scalar and just generate the assignment "closure_class.field := e" for
                // "Old(e)" to take a snapshot of e's value in the prestate. If it does depend on
                // any of the bound variables, then we need to generate a set of for-loops that
                // compute the indices and values of e for each tuple of indices so it can be retrieved
                // (given the indices) in the post-state.
                CollectBoundVariables cbv = new CollectBoundVariables(this.stackOfBoundVariables);
                cbv.VisitExpression(oldExpression.expression);

                SubstituteClosureClassWithinOldExpressions subst = new SubstituteClosureClassWithinOldExpressions(this.closureLocals);
                Expression e = subst.VisitExpression(oldExpression.expression);
                if (cbv.FoundVariables.Count == 0)
                {
                    // Use a scalar for the old variable

                    Local closureLocal;
                    if (!this.closureLocals.TryGetValue(this.topLevelClosureClass, out closureLocal))
                    {
                        Contract.Assume(false, "can't find closure local!");
                    }

                    // Define a scalar

                    var   clTemplate = HelperMethods.Unspecialize(this.topLevelClosureClass);
                    Field f          = new Field(clTemplate,
                                                 null,
                                                 FieldFlags.CompilerControlled | FieldFlags.Public,
                                                 Identifier.For("_old" + oldExpression.expression.UniqueKey.ToString()),
                                                 // unique name for this old expr.
                                                 oldExpression.Type,
                                                 null);

                    clTemplate.Members.Add(f);

                    // now produce properly instantiated field
                    f = (Field)Rewriter.GetMemberInstanceReference(f, this.topLevelClosureClass);

                    // Generate code to store value in prestate

                    this.prestateValuesOfOldExpressions.Statements.Add(
                        new AssignmentStatement(new MemberBinding(closureLocal, f), e));

                    // Return expression to be used in poststate

                    // Return an expression that will evaluate in the poststate to the value of the old
                    // expression in the prestate. This will be this.up.f where "up" is the field C#
                    // generated to point to the instance of the top-level closure class.
                    if (this.PointerToTopLevelClosureClass == null)
                    {
                        // then the old expression occurs in the top-level closure class. Just return "this.f"
                        // where "this" refers to the top-level closure class.
                        return(new MemberBinding(new This(this.currentClosureClass), f));
                    }
                    else
                    {
                        return(new MemberBinding(
                                   new MemberBinding(new This(this.currentClosureClass), this.PointerToTopLevelClosureClass),
                                   f));
                    }
                }
                else
                {
                    // the Old expression *does* depend upon at least one of the bound variable
                    // in a ForAll or Exists expression

                    // Use an indexed variable for the old variable

                    TypeNode oldVariableTypeDomain;

                    // Decide if domain is one-dimensional or not

                    bool oneDimensional = cbv.FoundVariables.Count == 1 && cbv.FoundVariables[0].Type.IsValueType;
                    if (oneDimensional)
                    {
                        // a one-dimensional old-expression can use the index variable directly
                        oldVariableTypeDomain = cbv.FoundVariables[0].Type;
                    }
                    else
                    {
                        oldVariableTypeDomain = SystemTypes.GenericList.GetTemplateInstance(this.module,
                                                                                            SystemTypes.Int32);
                    }

                    TypeNode oldVariableTypeRange = oldExpression.Type;
                    TypeNode oldVariableType      = SystemTypes.GenericDictionary.GetTemplateInstance(this.module,
                                                                                                      oldVariableTypeDomain, oldVariableTypeRange);

                    Local closureLocal;
                    if (!this.closureLocals.TryGetValue(this.topLevelClosureClass, out closureLocal))
                    {
                        Contract.Assume(false, "can't find closure local");
                    }

                    // Define an indexed variable

                    var clTemplate = HelperMethods.Unspecialize(this.topLevelClosureClass);

                    Field f = new Field(clTemplate,
                                        null,
                                        FieldFlags.CompilerControlled | FieldFlags.Assembly,
                                        // can't be private or protected because it needs to be accessed from inner (closure) classes that don't inherit from the class this field is added to.
                                        Identifier.For("_old" + oldExpression.expression.UniqueKey.ToString()),
                                        // unique name for this old expr.
                                        oldVariableType,
                                        null);

                    clTemplate.Members.Add(f);

                    // instantiate f
                    f = (Field)Rewriter.GetMemberInstanceReference(f, closureLocal.Type);

                    // Generate code to initialize the indexed variable

                    Statement init = new AssignmentStatement(
                        new MemberBinding(closureLocal, f),
                        new Construct(new MemberBinding(null, oldVariableType.GetConstructor()), null));

                    this.prestateValuesOfOldExpressions.Statements.Add(init);

                    // Generate code to store values in prestate

                    // Create assignment: this.closure.f[i,j,k,...] = e;

                    Method setItem = oldVariableType.GetMethod(Identifier.For("set_Item"), oldVariableTypeDomain, oldVariableTypeRange);

                    Expression index;
                    if (oneDimensional)
                    {
                        index = cbv.FoundVariables[0];
                    }
                    else
                    {
                        //InstanceInitializer ctor =
                        //  ContractNodes.TupleClass.GetConstructor(SystemTypes.Int32.GetArrayType(1));
                        //Expression index = new Construct(new MemberBinding(null,ctor),new ExpressionList(
                        index = Literal.Null;
                    }

                    MethodCall mc = new MethodCall(new MemberBinding(new MemberBinding(closureLocal, f), setItem),
                                                   new ExpressionList(index, e));

                    Statement stat = new ExpressionStatement(mc);

                    List <Local>     locals   = new List <Local>(this.stackOfBoundVariables.Count);
                    TrivialHashtable paramMap = new TrivialHashtable();

                    // Generate a local for each bound variable to use in for-loop

                    foreach (Variable v in this.stackOfBoundVariables)
                    {
                        Local l = new Local(Identifier.Empty, v.Type);
                        paramMap[v.UniqueKey] = l;
                        locals.Add(l);
                    }

                    // Substitute locals for bound variables in old expression *AND* in inner loop bounds

                    SubstituteParameters sps = new SubstituteParameters(paramMap, this.stackOfBoundVariables);
                    sps.Visit(stat);

                    // Create nested for-loops around assignment

                    // keep track of when the first variable is used (from innermost to outermost)
                    // as soon as the first one is needed because the old expression depends on it,
                    // then keep all enclosing loops. It would be possible to keep only those where
                    // the necessary loops have loop bounds that depend on an enclosing loop, but I
                    // haven't calculated that, so just keep them all. For instance, if the old expression
                    // depends on j and the loops are "for i,0,n" and inside that "for j,0,i", then need
                    // both loops. If the inner loop bounds were 0 and n, then wouldn't need the outer
                    // loop.
                    bool usedAVariable = false;

                    for (int i = this.stackOfBoundVariables.Count - 1; 0 <= i; i--)
                    {
                        if (!usedAVariable &&
                            !cbv.FoundVariables.Contains(this.stackOfBoundVariables[i]))
                        {
                            continue;
                        }
                        usedAVariable = true;
                        Expression lowerBound = new Duplicator(this.module, this.currentClosureClass).VisitExpression(
                            this.stackOfMethods[i].Operands[0]);

                        lowerBound = subst.VisitExpression(lowerBound);
                        lowerBound = sps.VisitExpression(lowerBound);

                        Expression upperBound = new Duplicator(this.module, this.currentClosureClass).VisitExpression(
                            this.stackOfMethods[i].Operands[1]);

                        upperBound = subst.VisitExpression(upperBound);
                        upperBound = sps.VisitExpression(upperBound);

                        stat = RewriteHelper.GenerateForLoop(locals[i], lowerBound, upperBound, stat);
                    }

                    this.prestateValuesOfOldExpressions.Statements.Add(stat);

                    // Return expression to be used in poststate

                    Method getItem = oldVariableType.GetMethod(Identifier.For("get_Item"), oldVariableTypeDomain);
                    if (oneDimensional)
                    {
                        index = cbv.FoundReferences[0];
                    }
                    else
                    {
                        //InstanceInitializer ctor =
                        //  ContractNodes.TupleClass.GetConstructor(SystemTypes.Int32.GetArrayType(1));
                        //Expression index = new Construct(new MemberBinding(null,ctor),new ExpressionList(
                        index = Literal.Null;
                    }

                    // Return an expression that will evaluate in the poststate to the value of the old
                    // expression in the prestate. This will be this.up.f[i,j,k,...] where "up" is the field C#
                    // generated to point to the instance of the top-level closure class.
                    MemberBinding thisDotF;

                    if (this.PointerToTopLevelClosureClass == null)
                    {
                        // then the old expression occurs in the top-level closure class. Just return "this.f"
                        // where "this" refers to the top-level closure class.
                        Contract.Assume(f != null);

                        thisDotF = new MemberBinding(new This(clTemplate), HelperMethods.Unspecialize(f));
                    }
                    else
                    {
                        thisDotF = new MemberBinding(
                            new MemberBinding(new This(clTemplate), this.PointerToTopLevelClosureClass),
                            f);
                    }

                    return(new MethodCall(new MemberBinding(thisDotF, getItem), new ExpressionList(index)));
                }
            }
            else
            {
                // Not in closure ==> Create a local variable

                Local l = GetLocalForOldExpression(oldExpression);

                // Make sure local can be seen in the debugger (for the entire method, unfortunately)

                if (currentMethod.LocalList == null)
                {
                    currentMethod.LocalList = new LocalList();
                }

                currentMethod.LocalList.Add(l);
                currentMethod.Body.HasLocals = true;

                this.prestateValuesOfOldExpressions.Statements.Add(new AssignmentStatement(l, oldExpression.expression));

                // Return an expression that will evaluate in the poststate to the value of the old
                // expression in the prestate. When we're not in a closure, this is just the local
                // itself.
                return(l);
            }
        }