private void VisitAssignment(ITargetExpression target, IExpression source, SourceTraverser sourceTraverser, bool treatAsStatement, bool pushTargetRValue, bool resultIsInitialTargetRValue) { Contract.Requires(target != null); Contract.Requires(source != null); Contract.Requires(sourceTraverser != null); Contract.Requires(!resultIsInitialTargetRValue || pushTargetRValue); Contract.Requires(!pushTargetRValue || source is IBinaryOperation); var tok = source.Token(); var typ = source.Type; var structCopy = TranslationHelper.IsStruct(typ) && !(source is IDefaultValue); // then a struct value of type S is being assigned: "lhs := s" // model this as the statement "call lhs := S..#copy_ctor(s)" that does the bit-wise copying Bpl.DeclWithFormals proc = null; if (structCopy) { proc = this.sink.FindOrCreateProcedureForStructCopy(typ); } object container = target.Definition; Top: ILocalDefinition/*?*/ local = container as ILocalDefinition; if (local != null) { if (source is IDefaultValue && !local.Type.ResolvedType.IsReferenceType) { // this.LoadAddressOf(local, null); // this.generator.Emit(OperationCode.Initobj, local.Type); // if (!treatAsStatement) this.LoadLocal(local); } else { Bpl.IdentifierExpr temp = null; var bplLocal = Bpl.Expr.Ident(this.sink.FindOrCreateLocalVariable(local)); if (pushTargetRValue) { this.TranslatedExpressions.Push(bplLocal); if (!treatAsStatement && resultIsInitialTargetRValue) { var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var e3 = this.TranslatedExpressions.Pop(); var cmd3 = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd3); this.TranslatedExpressions.Push(temp); } } sourceTraverser(source); var e = this.TranslatedExpressions.Pop(); if (temp != null) this.TranslatedExpressions.Push(temp); Bpl.Cmd cmd; if (structCopy) { cmd = new Bpl.CallCmd(tok, proc.Name, new List<Bpl.Expr> { e, }, new List<Bpl.IdentifierExpr> { bplLocal, }); } else { cmd = Bpl.Cmd.SimpleAssign(tok, bplLocal, e); } StmtTraverser.StmtBuilder.Add(cmd); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.TranslatedExpressions.Push(bplLocal); } } return; } IParameterDefinition/*?*/ parameter = container as IParameterDefinition; if (parameter != null) { if (source is IDefaultValue && !parameter.Type.ResolvedType.IsReferenceType) { //this.LoadAddressOf(parameter, null); //this.generator.Emit(OperationCode.Initobj, parameter.Type); //if (!treatAsStatement) this.LoadParameter(parameter); } else { Bpl.IdentifierExpr temp = null; if (pushTargetRValue) { this.LoadParameter(parameter); if (!treatAsStatement && resultIsInitialTargetRValue) { var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var e3 = this.TranslatedExpressions.Pop(); var cmd3 = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd3); this.TranslatedExpressions.Push(temp); } } sourceTraverser(source); var e = this.TranslatedExpressions.Pop(); if (temp != null) this.TranslatedExpressions.Push(temp); var bplParam = Bpl.Expr.Ident(this.sink.FindParameterVariable(parameter, this.contractContext)); Bpl.Cmd cmd; if (structCopy) { cmd = new Bpl.CallCmd(tok, proc.Name, new List<Bpl.Expr> { e, bplParam, }, new List<Bpl.IdentifierExpr>()); } else { cmd = Bpl.Cmd.SimpleAssign(tok, bplParam, e); } StmtTraverser.StmtBuilder.Add(cmd); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.LoadParameter(parameter); } } return; } IFieldReference/*?*/ field = container as IFieldReference; if (field != null) { var f = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(field)); var boogieTypeOfField = sink.CciTypeToBoogie(field.Type); if (source is IDefaultValue && !field.Type.ResolvedType.IsReferenceType) { //this.LoadAddressOf(field, target.Instance); //if (!treatAsStatement) { // this.generator.Emit(OperationCode.Dup); // this.StackSize++; //} //this.generator.Emit(OperationCode.Initobj, field.Type); //if (!treatAsStatement) // this.generator.Emit(OperationCode.Ldobj, field.Type); //else // this.StackSize--; } else { Bpl.Expr x = null; Bpl.IdentifierExpr temp = null; if (pushTargetRValue) { if (target.Instance != null) { this.Traverse(target.Instance); x = this.TranslatedExpressions.Pop(); AssertOrAssumeNonNull(tok, x); var e2 = this.sink.Heap.ReadHeap(x, f, TranslationHelper.IsStruct(field.ContainingType) ? AccessType.Struct : AccessType.Heap, boogieTypeOfField); this.TranslatedExpressions.Push(e2); } else { TranslatedExpressions.Push(f); } if (!treatAsStatement && resultIsInitialTargetRValue) { var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var e3 = this.TranslatedExpressions.Pop(); var cmd = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd); this.TranslatedExpressions.Push(temp); } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var e3 = this.TranslatedExpressions.Pop(); var cmd = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd); this.TranslatedExpressions.Push(temp); } var e = this.TranslatedExpressions.Pop(); if (temp != null) this.TranslatedExpressions.Push(temp); if (target.Instance == null) { // static fields are not kept in the heap StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, f, e)); } else { this.sink.Heap.WriteHeap(tok, x, f, e, field.ResolvedField.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, boogieTypeOfField, StmtTraverser.StmtBuilder); } } return; } VisitArrayIndexer: IArrayIndexer/*?*/ arrayIndexer = container as IArrayIndexer; if (arrayIndexer != null) { Contract.Assume(arrayIndexer.Indices.Count() == 1); // BUG: deal with multi-dimensional arrays if (source is IDefaultValue && !arrayIndexer.Type.ResolvedType.IsReferenceType) { // this.LoadAddressOf(arrayIndexer, target.Instance); // if (!treatAsStatement) { // this.generator.Emit(OperationCode.Dup); // this.StackSize++; // } // this.generator.Emit(OperationCode.Initobj, arrayIndexer.Type); // if (!treatAsStatement) // this.generator.Emit(OperationCode.Ldobj, arrayIndexer.Type); // else // this.StackSize--; } else { Bpl.IdentifierExpr/*?*/ temp = null; this.Traverse(arrayIndexer.IndexedObject); var arrayExpr = this.TranslatedExpressions.Peek(); this.Traverse(arrayIndexer.Indices); var indexExpr = this.TranslatedExpressions.Peek(); if (pushTargetRValue) { AssertOrAssumeNonNull(tok, arrayExpr); var e2 = this.sink.Heap.ReadHeap(arrayExpr, indexExpr, AccessType.Array, this.sink.CciTypeToBoogie(arrayIndexer.Type)); this.TranslatedExpressions.Push(e2); if (!treatAsStatement && resultIsInitialTargetRValue) { var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var e3 = this.TranslatedExpressions.Pop(); var cmd = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd); this.TranslatedExpressions.Push(temp); } } sourceTraverser(source); var e = this.TranslatedExpressions.Pop(); var indices_prime = this.TranslatedExpressions.Pop(); var x = this.TranslatedExpressions.Pop(); sink.Heap.WriteHeap(Bpl.Token.NoToken, x, indices_prime, e, AccessType.Array, sink.CciTypeToBoogie(arrayIndexer.Type), StmtTraverser.StmtBuilder); if (!treatAsStatement && !resultIsInitialTargetRValue) { AssertOrAssumeNonNull(tok, arrayExpr); var e2 = this.sink.Heap.ReadHeap(arrayExpr, indexExpr, AccessType.Array, this.sink.CciTypeToBoogie(arrayIndexer.Type)); this.TranslatedExpressions.Push(e2); } else { if (temp != null) this.TranslatedExpressions.Push(temp); } } return; } IAddressDereference/*?*/ addressDereference = container as IAddressDereference; if (addressDereference != null) { var addrOf = addressDereference.Address as IAddressOf; if (addrOf != null) { var arrayIndexer2 = addrOf.Expression.Definition as IArrayIndexer; if (arrayIndexer2 != null) { container = arrayIndexer2; goto VisitArrayIndexer; } } var be = addressDereference.Address as IBoundExpression; if (be != null) { container = be.Definition; goto Top; } this.Traverse(addressDereference.Address); if (source is IDefaultValue && !addressDereference.Type.ResolvedType.IsReferenceType) { //if (!treatAsStatement) { // this.generator.Emit(OperationCode.Dup); // this.StackSize++; //} //this.generator.Emit(OperationCode.Initobj, addressDereference.Type); //if (!treatAsStatement) // this.generator.Emit(OperationCode.Ldobj, addressDereference.Type); //else // this.StackSize--; } else if (source is IAddressDereference) { //if (!treatAsStatement) { // this.generator.Emit(OperationCode.Dup); // this.StackSize++; //} //this.Traverse(((IAddressDereference)source).Address); //this.generator.Emit(OperationCode.Cpobj, addressDereference.Type); //this.StackSize -= 2; //if (!treatAsStatement) // this.generator.Emit(OperationCode.Ldobj, addressDereference.Type); } else { Bpl.IdentifierExpr/*?*/ temp = null; if (pushTargetRValue) { this.TranslatedExpressions.Push(this.TranslatedExpressions.Peek()); if (!treatAsStatement && resultIsInitialTargetRValue) { this.TranslatedExpressions.Push(this.TranslatedExpressions.Peek()); var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var e3 = this.TranslatedExpressions.Pop(); var cmd = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd); this.TranslatedExpressions.Push(temp); } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.TranslatedExpressions.Push(this.TranslatedExpressions.Peek()); var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var e3 = this.TranslatedExpressions.Pop(); var cmd = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd); this.TranslatedExpressions.Push(temp); } //this.VisitAssignmentTo(addressDereference); if (temp != null) this.TranslatedExpressions.Push(temp); } return; } IPropertyDefinition/*?*/ propertyDefinition = container as IPropertyDefinition; if (propertyDefinition != null) { Contract.Assume(propertyDefinition.Getter != null && propertyDefinition.Setter != null); if (!propertyDefinition.IsStatic) { this.Traverse(target.Instance); } Bpl.IdentifierExpr temp = null; var token = Bpl.Token.NoToken; if (pushTargetRValue) { List<Bpl.Expr> inexpr; List<Bpl.IdentifierExpr> outvars; Bpl.IdentifierExpr thisExpr; Dictionary<Bpl.IdentifierExpr, Tuple<Bpl.IdentifierExpr, bool>> toBoxed; var proc2 = TranslateArgumentsAndReturnProcedure(token, propertyDefinition.Getter, propertyDefinition.Getter.ResolvedMethod, target.Instance, Enumerable<IExpression>.Empty, out inexpr, out outvars, out thisExpr, out toBoxed); EmitLineDirective(token); this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(token, proc2.Name, inexpr, outvars)); if (!treatAsStatement && resultIsInitialTargetRValue) { //var //this.generator.Emit(OperationCode.Dup); //this.StackSize++; //temp = new TemporaryVariable(source.Type, this.method); //this.VisitAssignmentTo(temp); } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { var e3 = this.TranslatedExpressions.Pop(); var loc = this.sink.CreateFreshLocal(source.Type); temp = Bpl.Expr.Ident(loc); var cmd = Bpl.Cmd.SimpleAssign(tok, temp, e3); this.StmtTraverser.StmtBuilder.Add(cmd); this.TranslatedExpressions.Push(temp); } var setterArgs = new List<Bpl.Expr>(); var setterArg = this.TranslatedExpressions.Pop(); if (!propertyDefinition.IsStatic) setterArgs.Add(this.TranslatedExpressions.Pop()); setterArgs.Add(setterArg); var setterProc = this.sink.FindOrCreateProcedure(propertyDefinition.Setter.ResolvedMethod); EmitLineDirective(token); this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(token, setterProc.Decl.Name, setterArgs, new List<Bpl.IdentifierExpr>())); if (temp != null) this.TranslatedExpressions.Push(temp); return; } Contract.Assume(false); }
private void VisitAssignment(ITargetExpression target, IExpression source, SourceTraverser sourceTraverser, bool treatAsStatement, bool pushTargetRValue, bool resultIsInitialTargetRValue) { Contract.Requires(target != null); Contract.Requires(source != null); Contract.Requires(sourceTraverser != null); Contract.Requires(!resultIsInitialTargetRValue || pushTargetRValue); Contract.Requires(!pushTargetRValue || source is IBinaryOperation); object container = target.Definition; ILocalDefinition/*?*/ local = container as ILocalDefinition; if (local != null) { if (source is IDefaultValue && !local.Type.ResolvedType.IsReferenceType) { this.LoadAddressOf(local, null); this.generator.Emit(OperationCode.Initobj, local.Type); this.StackSize--; if (!treatAsStatement) this.LoadLocal(local); } else { if (pushTargetRValue) { this.LoadLocal(local); if (!treatAsStatement && resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } this.VisitAssignmentTo(local); } return; } IParameterDefinition/*?*/ parameter = container as IParameterDefinition; if (parameter != null) { if (source is IDefaultValue && !parameter.Type.ResolvedType.IsReferenceType) { this.LoadAddressOf(parameter, null); this.generator.Emit(OperationCode.Initobj, parameter.Type); this.StackSize--; if (!treatAsStatement) this.LoadParameter(parameter); } else { if (pushTargetRValue) { this.LoadParameter(parameter); if (!treatAsStatement && resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } ushort parIndex = GetParameterIndex(parameter); if (parIndex <= byte.MaxValue) this.generator.Emit(OperationCode.Starg_S, parameter); else this.generator.Emit(OperationCode.Starg, parameter); this.StackSize--; } return; } IFieldReference/*?*/ field = container as IFieldReference; if (field != null) { if (source is IDefaultValue && !field.Type.ResolvedType.IsReferenceType) { this.LoadAddressOf(field, target.Instance); if (!treatAsStatement) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } this.generator.Emit(OperationCode.Initobj, field.Type); if (!treatAsStatement) this.generator.Emit(OperationCode.Ldobj, field.Type); else this.StackSize--; } else { ILocalDefinition/*?*/ temp = null; if (target.Instance != null) { this.Traverse(target.Instance); if (pushTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } } if (pushTargetRValue) { if (target.Instance != null) this.generator.Emit(OperationCode.Ldfld, field); else { this.generator.Emit(OperationCode.Ldsfld, field); this.StackSize++; } if (!treatAsStatement && resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(source.Type, this.method); this.VisitAssignmentTo(temp); } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(source.Type, this.method); this.VisitAssignmentTo(temp); } if (target.IsUnaligned) this.generator.Emit(OperationCode.Unaligned_, target.Alignment); if (target.IsVolatile) this.generator.Emit(OperationCode.Volatile_); if (target.Instance == null) { this.generator.Emit(OperationCode.Stsfld, field); this.StackSize--; } else { this.generator.Emit(OperationCode.Stfld, field); this.StackSize-=2; } if (temp != null) this.LoadLocal(temp); } return; } IArrayIndexer/*?*/ arrayIndexer = container as IArrayIndexer; if (arrayIndexer != null) { if (source is IDefaultValue && !arrayIndexer.Type.ResolvedType.IsReferenceType) { this.LoadAddressOf(arrayIndexer, target.Instance); if (!treatAsStatement) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } this.generator.Emit(OperationCode.Initobj, arrayIndexer.Type); if (!treatAsStatement) this.generator.Emit(OperationCode.Ldobj, arrayIndexer.Type); else this.StackSize--; } else { ILocalDefinition/*?*/ temp = null; IArrayTypeReference arrayType = (IArrayTypeReference)target.Instance.Type; this.Traverse(target.Instance); this.Traverse(arrayIndexer.Indices); if (pushTargetRValue) { if (arrayType.IsVector) this.generator.Emit(OperationCode.Ldelema); else this.generator.Emit(OperationCode.Array_Addr, arrayType); this.generator.Emit(OperationCode.Dup); this.StackSize++; this.LoadIndirect(arrayType.ElementType); if (!treatAsStatement && resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(source.Type, this.method); this.VisitAssignmentTo(temp); } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(source.Type, this.method); this.VisitAssignmentTo(temp); } if (pushTargetRValue) { this.StoreIndirect(arrayType.ElementType); } else { if (arrayType.IsVector) this.StoreVectorElement(arrayType.ElementType); else { this.generator.Emit(OperationCode.Array_Set, arrayType); this.StackSize-=(ushort)(IteratorHelper.EnumerableCount(arrayIndexer.Indices)+2); } } if (temp != null) this.LoadLocal(temp); } return; } IAddressDereference/*?*/ addressDereference = container as IAddressDereference; if (addressDereference != null) { this.Traverse(addressDereference.Address); if (source is IDefaultValue && !addressDereference.Type.ResolvedType.IsReferenceType) { if (!treatAsStatement) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } this.generator.Emit(OperationCode.Initobj, addressDereference.Type); if (!treatAsStatement) this.generator.Emit(OperationCode.Ldobj, addressDereference.Type); else this.StackSize--; } else if (source is IAddressDereference) { if (!treatAsStatement) { this.generator.Emit(OperationCode.Dup); this.StackSize++; } this.Traverse(((IAddressDereference)source).Address); this.generator.Emit(OperationCode.Cpobj, addressDereference.Type); this.StackSize-=2; if (!treatAsStatement) this.generator.Emit(OperationCode.Ldobj, addressDereference.Type); } else { ILocalDefinition/*?*/ temp = null; if (pushTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; if (addressDereference.IsUnaligned) this.generator.Emit(OperationCode.Unaligned_, addressDereference.Alignment); if (addressDereference.IsVolatile) this.generator.Emit(OperationCode.Volatile_); this.LoadIndirect(addressDereference.Type); if (!treatAsStatement && resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(source.Type, this.method); this.VisitAssignmentTo(temp); } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(source.Type, this.method); this.VisitAssignmentTo(temp); } this.VisitAssignmentTo(addressDereference); if (temp != null) this.LoadLocal(temp); } return; } IPropertyDefinition/*?*/ propertyDefinition = container as IPropertyDefinition; if (propertyDefinition != null) { Contract.Assume(propertyDefinition.Getter != null && propertyDefinition.Setter != null); if (!propertyDefinition.IsStatic) { this.Traverse(target.Instance); } ILocalDefinition temp = null; if (pushTargetRValue) { if (!propertyDefinition.IsStatic) { this.generator.Emit(OperationCode.Dup); this.StackSize++; this.generator.Emit(target.GetterIsVirtual ? OperationCode.Callvirt : OperationCode.Call, propertyDefinition.Getter); } else { this.generator.Emit(OperationCode.Call, propertyDefinition.Getter); this.StackSize++; } if (!treatAsStatement && resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(source.Type, this.method); this.VisitAssignmentTo(temp); } } sourceTraverser(source); if (!treatAsStatement && !resultIsInitialTargetRValue) { this.generator.Emit(OperationCode.Dup); this.StackSize++; temp = new TemporaryVariable(propertyDefinition.Type, this.method); this.VisitAssignmentTo(temp); } if (!propertyDefinition.IsStatic) { this.generator.Emit(target.SetterIsVirtual ? OperationCode.Callvirt : OperationCode.Call, propertyDefinition.Setter); this.StackSize -= 2; } else { this.generator.Emit(OperationCode.Call, propertyDefinition.Setter); this.StackSize--; } if (temp != null) this.LoadLocal(temp); return; } Contract.Assume(false); }