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; }