Exemple #1
0
    public override Statement VisitForEach(ForEach forEach) {
      if (forEach == null || forEach.Body == null) return null;
      //First transform
      Block continueTarget = new Block(new StatementList(0));
      Block exitTarget = new Block(new StatementList(0));
      StatementList statements = new StatementList(16);
      StatementList generatedInvariants = new StatementList(1);
      Block result = new Block(statements);
      result.HasLocals = true;
      //initialize and update induction variable
      if (forEach.InductionVariable != null) {
        TypeNode inductionVariableType = ((OptionalModifier)forEach.InductionVariable.Type).ModifiedType;
        TypeNode inductionVariableTypeView = this.GetTypeView(inductionVariableType);
        statements.Add(new AssignmentStatement(forEach.InductionVariable, new Construct(new MemberBinding(null, inductionVariableTypeView.GetConstructor()), null, inductionVariableType)));
        forEach.Body.Statements.Add(new ExpressionStatement(new MethodCall(new MemberBinding(forEach.InductionVariable, inductionVariableTypeView.GetMethod(StandardIds.Add, forEach.TargetVariableType)), new ExpressionList(forEach.TargetVariable), NodeType.Call, SystemTypes.Void)));
      }
      //get enumerator. Either call getEnumerator, or use the object itself. 
      CollectionEnumerator cEnumerator = forEach.SourceEnumerable as CollectionEnumerator;
      if (cEnumerator == null || cEnumerator.Collection == null) return null;
      TypeNode tt = forEach.TargetVariable == null || forEach.TargetVariable.Type == null ? null : forEach.TargetVariable.Type.Template;
      bool suppressNullElements = tt != null && tt == SystemTypes.GenericNonNull;
      Expression enumerator;
      Expression length = null;
      Expression index = null;
      BlockScope tempScope = forEach.ScopeForTemporaryVariables;
      Method getEnumerator = cEnumerator.GetEnumerator;
      if (getEnumerator != null && this.currentMethod.Scope != null && this.currentMethod.Scope.CapturedForClosure) {
        TypeNode geDT = this.currentMethod.Scope.FixTypeReference(getEnumerator.DeclaringType);
        if (geDT != getEnumerator.DeclaringType) {
          getEnumerator = cEnumerator.GetEnumerator = geDT.GetMethod(getEnumerator.Name);
        }
      }
      Identifier id = null;

      TypeNode collectionType = cEnumerator.Collection.Type;
      ArrayType arrType = null;
      if (collectionType != null) {
        arrType = this.typeSystem.GetUnderlyingType(collectionType) as ArrayType;
        if (arrType != null) {
          collectionType = arrType;
        }
      }
      if (this.currentMethod.Scope != null && this.currentMethod.Scope.CapturedForClosure) 
        collectionType = this.currentMethod.Scope.FixTypeReference(collectionType);

      if (getEnumerator != null) {
        Local ctemp = new Local(Identifier.Empty, collectionType, result);
        Expression e = this.VisitExpression(cEnumerator.Collection);
        if (e.Type is Reference)
          e = new AddressDereference(e, arrType);
        AssignmentStatement assignCollection = new AssignmentStatement(ctemp, e);
        assignCollection.SourceContext = forEach.SourceContext;
        assignCollection.SourceContext.EndPos = forEach.SourceContext.StartPos + this.foreachLength;
        statements.Add(assignCollection);


        if (!ctemp.Type.IsValueType && forEach.StatementTerminatesNormallyIfEnumerableIsNull)
          statements.Add(new Branch(new UnaryExpression(this.Box(ctemp), NodeType.LogicalNot), exitTarget));
        MemberBinding mb = new MemberBinding(ctemp, getEnumerator);
        if (ctemp.Type.IsValueType)
          mb.TargetObject = new UnaryExpression(ctemp, NodeType.AddressOf, ctemp.Type.GetReferenceType());
        else if (ctemp.Type is ITypeParameter)
          mb.TargetObject = this.Box(ctemp);
        MethodCall callGetEnumerator = new MethodCall(mb, null, getEnumerator.IsVirtualAndNotDeclaredInStruct ? NodeType.Callvirt : NodeType.Call);
        callGetEnumerator.Type = getEnumerator.ReturnType;
        callGetEnumerator.SourceContext = cEnumerator.Collection.SourceContext;
        id = Identifier.For("foreachEnumerator: " +forEach.GetHashCode());
        if (tempScope.CapturedForClosure) {
          Field f = new Field(tempScope, null, FieldFlags.CompilerControlled | FieldFlags.SpecialName, id, this.currentMethod.Scope.FixTypeReference(getEnumerator.ReturnType), null);
          if (f.Type != SystemTypes.String) { // Should be any immutable type, but string is the only one so far!
            f.Attributes.Add(
              new AttributeNode(new MemberBinding(null, SystemTypes.PeerAttribute.GetConstructor()),null, AttributeTargets.Field));
          }
          enumerator = this.VisitMemberBinding(new MemberBinding(new ImplicitThis(), f));
        } else
          enumerator = new Local(id, getEnumerator.ReturnType, result);
        if (enumerator == null) return null;
        statements.Add(new AssignmentStatement(enumerator, callGetEnumerator, NodeType.Nop, callGetEnumerator.SourceContext));
        if (!enumerator.Type.IsValueType && forEach.StatementTerminatesNormallyIfEnumeratorIsNull)
          statements.Add(new Branch(new UnaryExpression(enumerator, NodeType.LogicalNot), exitTarget));

        CompilerOptions options = this.currentCompilation == null ? null : this.currentCompilation.CompilerParameters as CompilerOptions;
        if (options != null && !options.DisableInternalContractsMetadata && !options.DisableGuardedClassesChecks && !tempScope.CapturedForClosure) {
          // Add loop invariants that the compiler knows about because it is generating the code
          // Don't generate any runtime checks for them, just the serialized form the static verification needs to see
          //
          // Don't do this for value types: they can't have invariants anyway (and translation fails for box expressions anyway)
          if (!enumerator.Type.IsValueType) {
            MemberList ms = SystemTypes.Guard.GetMembersNamed(Runtime.IsPeerConsistentId);
            if (ms != null && ms.Count == 1) {
              Method isConsistent = (Method)ms[0];
              Expression arg = enumerator;
              if (arg.NodeType == NodeType.Local) {
                Local l = (Local)arg;
                // all user-written locals are represented as memberbindings of a particular form
                // Boogie has built that assumption in so that the only true "Locals" that it sees
                // it considers special, like "return value".
                Field f = new Field(forEach.ScopeForTemporaryVariables, null, FieldFlags.CompilerControlled, l.Name, l.Type, Literal.Null);
                arg = new MemberBinding(null, f);
              }
              MethodCall mc = new MethodCall(new MemberBinding(null, isConsistent), new ExpressionList(arg), NodeType.Call, SystemTypes.Boolean);
              Assertion assertion = new Assertion(mc);
              assertion.SourceContext = assignCollection.SourceContext;
              foreach (Statement s in this.SerializeAssertion(this.currentModule, mc, "Foreach enumerator must be peer consistent.", assignCollection.SourceContext, "LoopInvariant")) {
                generatedInvariants.Add(s);
              }
            }
          }
        }

      } else if (cEnumerator.MoveNext == null) {
        Identifier indexId = Identifier.For("foreachIndex: " + forEach.GetHashCode());
        if (tempScope.CapturedForClosure) {
          id = Identifier.For("foreachLength: " + forEach.GetHashCode());
          Field f = new Field(tempScope, null, FieldFlags.CompilerControlled, id, SystemTypes.Int32, null);
          length = this.VisitMemberBinding(new MemberBinding(new ImplicitThis(), f));
          f = new Field(tempScope, null, FieldFlags.CompilerControlled, indexId, SystemTypes.Int32, null);
          index = this.VisitMemberBinding(new MemberBinding(new ImplicitThis(), f));
          id = Identifier.For("foreachEnumerator: " + forEach.GetHashCode());
          f = new Field(tempScope, null, FieldFlags.CompilerControlled | FieldFlags.SpecialName, id, collectionType, null);
          enumerator = this.VisitMemberBinding(new MemberBinding(new ImplicitThis(), f, cEnumerator.SourceContext));
          if (enumerator == null) return null;  // this is happening when foreach encloses anon delegate. just fixing the crash.
        } else {
          length = new Local(Identifier.Empty, SystemTypes.Int32);
          index = new Local(indexId, SystemTypes.Int32, result);
          enumerator = new Local(Identifier.Empty, collectionType, cEnumerator.SourceContext);
        }

        if (!(this.currentCompilation != null && this.currentCompilation.CompilerParameters is CompilerOptions && ((CompilerOptions)this.currentCompilation.CompilerParameters).DisableInternalContractsMetadata)) {
          // Add loop invariants that the compiler knows about because it is generating the code
          // Don't generate any runtime checks for them, just the serialized form the static verification needs to see
          //
          Expression arg = index;
          if (index.NodeType == NodeType.Local) {
            Local l = (Local)index;
            // all user-written locals are represented as memberbindings of a particular form
            // Boogie has built that assumption in so that the only true "Locals" that it sees
            // it considers special, like "return value".
            Field f = new Field(forEach.ScopeForTemporaryVariables, null, FieldFlags.CompilerControlled, l.Name, l.Type, Literal.Null);
            arg = new MemberBinding(null, f);
          }
          Assertion assertion = new Assertion(new BinaryExpression(arg, new Literal(0, SystemTypes.Int32), NodeType.Ge, SystemTypes.Boolean));
          assertion.SourceContext = forEach.SourceContext;
          assertion.SourceContext.EndPos = forEach.SourceContext.StartPos + this.foreachLength;
          foreach (Statement s in this.SerializeAssertion(this.currentModule, assertion.Condition, "Foreach loop index must be at least zero.", assertion.SourceContext, "LoopInvariant")) {
            generatedInvariants.Add(s);
          }
        }

        Expression e = this.VisitExpression(cEnumerator.Collection);
        if (e.Type is Reference)
          e = new AddressDereference(e, arrType);
        AssignmentStatement assignEnumerator = new AssignmentStatement(enumerator, e);
        assignEnumerator.SourceContext = forEach.SourceContext;
        if (forEach.SourceContext.StartPos != cEnumerator.SourceContext.StartPos || forEach.SourceContext.EndPos != cEnumerator.SourceContext.EndPos)
          assignEnumerator.SourceContext.EndPos = forEach.SourceContext.StartPos + this.foreachLength;
        statements.Add(assignEnumerator);
        statements.Add(new Branch(new UnaryExpression(enumerator, NodeType.LogicalNot), exitTarget));
        TypeNode et = this.ForeachArrayElementType(cEnumerator);
        if (et != null)
          statements.Add(new AssignmentStatement(length, new UnaryExpression(new UnaryExpression(enumerator, NodeType.Ldlen, SystemTypes.IntPtr), NodeType.Conv_I4,SystemTypes.Int32)));
        else {
          Debug.Assert(false);
          return null;
        }
        statements.Add(new AssignmentStatement(index, new Literal(0, SystemTypes.Int32)));
      } else {
        Field f = null;
        id = Identifier.For("foreachEnumerator: " + forEach.GetHashCode());
        if (tempScope.CapturedForClosure) {
          f = new Field(tempScope, null, FieldFlags.Private | FieldFlags.SpecialName, id, collectionType, null);
          enumerator = this.VisitMemberBinding(new MemberBinding(new ImplicitThis(), f));
          if (enumerator == null) return null;  // this can happen. See above for similar statement
        } else {
          f = new Field(tempScope, null, FieldFlags.Private | FieldFlags.SpecialName, id, collectionType, null);
          enumerator = this.VisitMemberBinding(new MemberBinding(new ImplicitThis(), f));
          //enumerator = new Local(id, collectionType);
        }
        AssignmentStatement assignEnumerator = new AssignmentStatement(enumerator, this.VisitExpression(cEnumerator.Collection));
        assignEnumerator.SourceContext = forEach.SourceContext;
        if (forEach.SourceContext.StartPos != cEnumerator.SourceContext.StartPos || forEach.SourceContext.EndPos != cEnumerator.SourceContext.EndPos)
          assignEnumerator.SourceContext.EndPos = forEach.SourceContext.StartPos + this.foreachLength;
        statements.Add(assignEnumerator);
        if (!enumerator.Type.IsValueType)
          statements.Add(new Branch(new UnaryExpression(enumerator, NodeType.LogicalNot), exitTarget));

        if (!(this.currentCompilation != null && this.currentCompilation.CompilerParameters is CompilerOptions && ((CompilerOptions)this.currentCompilation.CompilerParameters).DisableInternalContractsMetadata)) {
          // Add loop invariants that the compiler knows about because it is generating the code
          // Don't generate any runtime checks for them, just the serialized form the static verification needs to see
        }
      }
      //continueTarget
      statements.Add(continueTarget);

      if (generatedInvariants.Count > 0) {
        foreach (Statement s in generatedInvariants)
          statements.Add(s);
      }
      if (forEach.Invariants != null)
        statements.Add(this.VisitLoopInvariants(forEach.Invariants));

      if (length != null) {
        //if index >= length goto exitTarget
        Branch b = (index.Type.IsPrimitiveUnsignedInteger && length.Type.IsPrimitiveUnsignedInteger)?
          new Branch(new BinaryExpression(index, length, NodeType.Ge), exitTarget, exitTarget.SourceContext, true):
          new Branch(new BinaryExpression(index, length, NodeType.Ge), exitTarget);
        if (forEach.TargetVariable != null) {
          b.SourceContext = forEach.TargetVariable.SourceContext;
          if (forEach.SourceEnumerable.SourceContext.EndPos > b.SourceContext.EndPos)
            b.SourceContext.EndPos = forEach.SourceEnumerable.SourceContext.EndPos;
        }
        statements.Add(b);
        this.ForeachBodyHook(forEach, statements, enumerator, index);
        //target = enumerator[index]
        Debug.Assert(cEnumerator.ElementLocal != null);
        Expression target = cEnumerator.ElementLocal;
        ExpressionList args = new ExpressionList(1);
        args.Add(index);
        TypeNode et = this.ForeachArrayElementType(cEnumerator);
        if (et != null) {
          Expression elem = new Indexer(enumerator, args, et);
          if (et.IsValueType && !et.IsPrimitive)
            elem = new AddressDereference(new UnaryExpression(elem, NodeType.AddressOf, elem.Type.GetReferenceType()), et);
          AssignmentStatement indexIntoArray = new AssignmentStatement(target, elem);
          indexIntoArray.SourceContext = forEach.TargetVariable.SourceContext;
          if (forEach.SourceEnumerable.SourceContext.EndPos > indexIntoArray.SourceContext.EndPos)
            indexIntoArray.SourceContext.EndPos = forEach.SourceEnumerable.SourceContext.EndPos;
          statements.Add(indexIntoArray);
        }else{
          Debug.Assert(false);
          return null;
        }
        statements.Add(new AssignmentStatement(index, new BinaryExpression(index, new Literal(1, SystemTypes.Int32), NodeType.Add)));
      } else {
        //if !enumerator.MoveNext() goto exitTarget
        Method moveNext = cEnumerator.MoveNext;
        MemberBinding mb = new MemberBinding(enumerator, moveNext);
        MethodCall callMoveNext = new MethodCall(mb, null, moveNext.IsVirtualAndNotDeclaredInStruct ? NodeType.Callvirt : NodeType.Call);
        callMoveNext.Type = SystemTypes.Boolean;
        if (this.useGenerics && mb.TargetObject != null && mb.TargetObject.Type is ITypeParameter) {
          callMoveNext.Constraint = mb.TargetObject.Type;
          mb.TargetObject = new UnaryExpression(mb.TargetObject, NodeType.AddressOf, mb.TargetObject.Type.GetReferenceType());
        } else if (mb.TargetObject.Type.IsValueType) {
          mb.TargetObject = new UnaryExpression(mb.TargetObject, NodeType.AddressOf, mb.TargetObject.Type.GetReferenceType());
        }
        Branch b = new Branch(new UnaryExpression(callMoveNext, NodeType.LogicalNot), exitTarget);
        if (forEach.TargetVariable != null) {
          b.SourceContext = forEach.TargetVariable.SourceContext;
          if (forEach.SourceEnumerable.SourceContext.EndPos > b.SourceContext.EndPos)
            b.SourceContext.EndPos = forEach.SourceEnumerable.SourceContext.EndPos;
        }
        statements.Add(b);
        //target = enumerator.Current
        Debug.Assert(cEnumerator.GetCurrent != null);
        Method getCurrent = cEnumerator.GetCurrent;
        if (this.currentMethod.Scope != null && this.currentMethod.Scope.CapturedForClosure) {
          TypeNode gcDT = this.currentMethod.Scope.FixTypeReference(getCurrent.DeclaringType);
          if (gcDT != getCurrent.DeclaringType) {
            getCurrent = cEnumerator.GetCurrent = gcDT.GetMethod(getCurrent.Name);
            cEnumerator.ElementLocal.Type = getCurrent.ReturnType;
          }
        }
        mb = new MemberBinding(enumerator, getCurrent);
        MethodCall callGetCurrent = new MethodCall(mb, null, getCurrent.IsVirtualAndNotDeclaredInStruct ?  NodeType.Callvirt : NodeType.Call);
        if (this.useGenerics && mb.TargetObject != null && mb.TargetObject.Type is ITypeParameter) {
          callGetCurrent.Constraint = mb.TargetObject.Type;
          mb.TargetObject = new UnaryExpression(mb.TargetObject, NodeType.AddressOf, mb.TargetObject.Type.GetReferenceType());
        } else if (mb.TargetObject.Type.IsValueType) {
          mb.TargetObject = new UnaryExpression(mb.TargetObject, NodeType.AddressOf, mb.TargetObject.Type.GetReferenceType());
        }
        Debug.Assert(cEnumerator.ElementLocal != null);
        statements.Add(new AssignmentStatement(cEnumerator.ElementLocal, callGetCurrent, forEach.TargetVariable.SourceContext));
        //loop back if element null
        if (suppressNullElements)
          statements.Add(new Branch(new UnaryExpression(cEnumerator.ElementLocal, NodeType.LogicalNot), continueTarget));
      }
      if (forEach.TargetVariable != null) {
        Debug.Assert(cEnumerator.ElementCoercion != null);
        statements.Add(new AssignmentStatement(this.VisitTargetExpression(forEach.TargetVariable),
          this.VisitExpression(cEnumerator.ElementCoercion),forEach.TargetVariable.SourceContext));
      }
      //body
      this.continueTargets.Add(continueTarget);
      this.exitTargets.Add(exitTarget);
      statements.Add(this.VisitBlock(forEach.Body));
      this.continueTargets.Count--;
      this.exitTargets.Count--;
      //loop back
      statements.Add(new Branch(null, continueTarget));
      //exitTarget
      statements.Add(exitTarget);
      return result;
    }