-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMail.dfy
More file actions
320 lines (277 loc) · 8.24 KB
/
Mail.dfy
File metadata and controls
320 lines (277 loc) · 8.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/*===============================================
M.EIC037: Formal Methods For Critical Systems
Project 2
Your name(s):
===============================================*/
include "List.dfy"
import opened L = List
// The next three classes have a minimal class definition,
// for simplicity
class Address
{
constructor () {}
}
class Date
{
constructor () {}
}
class MessageId
{
constructor () {}
}
//==========================================================
// Message
//==========================================================
class Message
{
var id: MessageId
var content: string
var date: Date
var sender: Address
var recipients: seq<Address>
constructor (s: Address)
ensures fresh(id)
ensures fresh(date)
ensures content == ""
ensures sender == s
ensures recipients == []
{
id := new MessageId();
date := new Date();
content := "";
sender := s;
recipients := [];
}
method setContent(c: string)
modifies this
ensures content == c
ensures {id, date, sender} == old({id, date, sender})
ensures recipients == old(recipients)
{
content := c;
}
method setDate(d: Date)
modifies this
ensures date == d
ensures {id, sender} == old({id, sender})
ensures recipients == old(recipients)
ensures content == old(content)
{
date := d;
}
method addRecipient(p: nat, r: Address)
modifies this
requires p < |recipients|
ensures |recipients| == |old(recipients)| + 1
ensures forall i :: 0 <= i < p ==> recipients[i] == old(recipients[i])
ensures recipients[p] == r
ensures forall i :: p < i < |recipients| ==> recipients[i] == old(recipients[i-1])
ensures {id, date, sender} == old({id, date, sender})
ensures content == old(content)
{
recipients := recipients[..p] + [r] + recipients[p..];
}
}
//==========================================================
// Mailbox
//==========================================================
//
// Each Mailbox has a name, which is a string.
// Its main content is a set of messages.
//
class Mailbox {
const name: string
var messages: set<Message>
// Class invariant: All messages must be non-null
// invariant forall m :: m in messages ==> m != null;
// Creates an empty mailbox with name n
constructor (n: string)
ensures name == n
ensures messages == {}
{
name := n;
messages := {};
}
// Adds message m to the mailbox
method add(m: Message)
requires m !in messages
requires m != null
modifies this
ensures messages == old(messages) + {m}
ensures name == old(name)
ensures m in messages
{
messages := { m } + messages;
}
// Removes message m from mailbox
// m need not be in the mailbox
method remove(m: Message)
requires m in messages
modifies this
ensures messages == old(messages) - {m}
ensures name == old(name)
decreases messages
{
messages := messages - { m };
}
// Empties the mailbox
method empty()
modifies this
ensures messages == {}
ensures name == old(name)
{
messages := {};
}
}
//==========================================================
// MailApp
//==========================================================
class MailApp {
// abstract field for user defined boxes
ghost var userBoxes: set<Mailbox>
// abstract function returning all system mailboxes in one set
ghost function systemBoxes(): set<Mailbox>
reads this
{ {inbox, drafts, trash, sent} }
// the inbox, drafts, trash and sent are both abstract and concrete
var inbox: Mailbox
var drafts: Mailbox
var trash: Mailbox
var sent: Mailbox
// userboxList implements userBoxes
var userboxList: List<Mailbox>
// Class invariant
ghost predicate isValid()
reads this
{
// replace each `true` by your formulation of the invariants
// described below
//----------------------------------------------------------
// Abstract state invariants
//----------------------------------------------------------
// 1. all system mailboxes (inbox, ..., sent) are distinct
|systemBoxes()| == 4
//&& true
// 2. none of the system mailboxes are in the set
// of user-defined mailboxes
&& (systemBoxes() * userBoxes == {})
//----------------------------------------------------------
// Abstract-to-concrete state invariants
//----------------------------------------------------------
// userBoxes is the set of mailboxes in userboxList
&& forall l :: l in elements(userboxList) ==> l in userBoxes && |userBoxes| == |elements(userboxList)|
}
constructor ()
ensures fresh(inbox) && inbox.name == "Inbox"
ensures fresh(drafts) && drafts.name == "Drafts"
ensures fresh(trash) && trash.name == "Trash"
ensures fresh(sent) && sent.name == "Sent"
ensures inbox.messages == {} && drafts.messages == {} && trash.messages == {} && sent.messages == {}
ensures len(userboxList) == 0
ensures fresh(userBoxes)
ensures isValid()
{
inbox := new Mailbox("Inbox");
drafts := new Mailbox("Drafts");
trash := new Mailbox("Trash");
sent := new Mailbox("Sent");
userboxList := Nil;
userBoxes := {};
}
// Deletes user-defined mailbox mb
method deleteMailbox(mb: Mailbox)
requires isValid()
requires mb !in systemBoxes()
modifies this
requires mb in elements(userboxList)
ensures forall e :: e in elements(userboxList) ==> e != mb
ensures isValid()
{
userboxList := remove(userboxList, mb);
userBoxes := elements(userboxList);
}
// Adds a new mailbox with name n to set of user-defined mailboxes
// provided that no user-defined mailbox has name n already
method newMailbox(n: string)
requires isValid()
requires forall e :: e in elements(userboxList) ==> e.name != n
modifies this
ensures exists mb: Mailbox :: mb in userBoxes && mb.name == n && mb.messages == {}
ensures exists e :: e in elements(userboxList) ==> e.name == n
ensures isValid()
{
var mb := new Mailbox(n);
userboxList := Cons(mb, userboxList);
userBoxes := elements(userboxList);
}
// Adds a new message with sender s to the drafts mailbox
method newMessage(s: Address)
requires isValid()
modifies this, drafts
ensures |old(drafts.messages)| + 1 == |drafts.messages|
ensures isValid()
ensures exists nw: Message :: nw in drafts.messages
ensures old(drafts.messages) == {} ==> exists nw: Message :: drafts.messages == {nw}
{
var m := new Message(s);
drafts.add(m);
}
// Moves message m from mailbox mb1 to a different mailbox mb2
method moveMessage (m: Message, mb1: Mailbox, mb2: Mailbox)
requires isValid()
requires m in mb1.messages && !(m in mb2.messages)
modifies mb1, mb2
ensures m in mb2.messages && !(m in mb1.messages)
ensures isValid()
{
mb1.remove(m);
mb2.add(m);
}
// Moves message m from non-null mailbox mb to the trash mailbox
// provided that mb is not the trash mailbox
method deleteMessage (m: Message, mb: Mailbox)
requires isValid()
requires m in mb.messages && m !in trash.messages
modifies mb, trash
ensures m !in mb.messages && m in trash.messages
ensures isValid()
{
moveMessage(m, mb, trash);
}
// Moves message m from the drafts mailbox to the sent mailbox
method sendMessage(m: Message)
requires isValid()
requires m in drafts.messages && m !in sent.messages
modifies drafts, sent
ensures m in sent.messages && m !in drafts.messages
ensures isValid()
{
moveMessage(m, drafts, sent);
}
// Empties the trash mailbox
method emptyTrash ()
requires isValid()
modifies trash
ensures trash.messages == {}
ensures isValid()
{
trash.empty();
}
}
// Test
/* Can be used to test your code. */
method test() {
var ma := new MailApp();
assert ma.inbox.name == "Inbox";
assert ma.drafts.name == "Drafts";
assert ma.trash.name == "Trash";
assert ma.sent.name == "Sent";
assert ma.inbox.messages == ma.drafts.messages ==
ma.trash.messages ==
ma.sent.messages == {};
ma.newMailbox("students");
assert exists mb: Mailbox :: mb in ma.userBoxes && mb.name == "students" && mb.messages == {};
var s := new Address();
ma.newMessage(s);
assert exists nw: Message :: ma.drafts.messages == {nw};
}