Skip to content

Commit 636ab26

Browse files
committed
Add DFA Diagrams
1 parent 0992f29 commit 636ab26

File tree

6 files changed

+15
-5
lines changed

6 files changed

+15
-5
lines changed

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,19 @@ Let's explore how to use **state refinements** to specify and verify properties
128128
129129
Here, we specify that this object can only be in two states: `on` or `off`. Then, in the constructor, we specify that the initial state is `off`, through the `@StateRefinement` annotation. This annotation allows us to specify in which state the object should be before the method is called (`from`), and in which state it will be after the method execution (`to`). In the constructor, since it's the first method to be called, we can only specify the `to` state.
130130

131-
This object has two methods, `turnOn` and `turnOff`. From the state refinements, we can see that the method `turnOn` can only be called when the object is in state `off` transiting to state `on`. Similarly, the method `turnOff` can only be called when the object is in state `on`, transiting to state `off`. This means that we cannot call the same method twice in a row, since it would violate the protocol established by the state refinements.
131+
This object has two methods, `turnOn` and `turnOff`. From the state refinements, we can see that the method `turnOn` can only be called when the object is in state `off` transiting to state `on`. Similarly, the method `turnOff` can only be called when the object is in state `on`, transiting to state `off`. This means that we cannot call the same method twice in a row, since it would violate the protocol established by the state refinements. The following DFA illustrates this:
132+
133+
![Light Bulb DFA](./images/light_bulb_dfa.png)
132134

133135
> Uncomment line 22 to observe the error and then comment it back again.
134136
135137
#### Exercise
136138

137139
> Open [MediaPlayer.java](./src/main/java/com/tutorial/part2/exercise/MediaPlayer.java). Your task is to replace the `"true"` refinements with the appropriate ones to ensure the correct behavior of the `play`, `pause`, `resume` and `stop` methods, using the `stopped`, `playing`, and `paused` states.
138140
139-
For example, we want to ensure that the `pause` method can only be called when the player is playing, and that the `stop` method can only be called when the player is not stopped (you can use the `!` operator for this).
141+
For example, we want to ensure that the `pause` method can only be called when the player is playing, and that the `stop` method can only be called when the player is not stopped (you can either use the `!` or the `||` operator for this). The state transitions are represented by the following DFA:
142+
143+
![Media Player DFA](./images/media_player_dfa.png)
140144

141145
With the correct implementation, LiquidJava will report an error in line 30, since we are trying to resume playback when the player is stopped.
142146

@@ -146,7 +150,9 @@ To demonstrate the state refinements in a real world scenario, let's learn about
146150

147151
> Open [SocketRefinements.java](./src/main/java/com/tutorial/part3/SocketRefinements.java).
148152
149-
Here, we refine the `Socket` class through state refinements, with the possible states being `unconnected`, `bound`, `connected`, and `closed`. Then, for each method, we specify the allowed state transitions. This way, we can ensure that, for example, the `connect` method can only be called after the `bind` method, and that the `close` method can only be called once.
153+
Here, we refine the `Socket` class through state refinements, with the possible states being `unconnected`, `bound`, `connected`, and `closed`. Then, for each method, we specify the allowed state transitions. This way, we can ensure that, for example, the `connect` method can only be called after the `bind` method, and that the `close` method can only be called once. The following DFA details the allowed state transitions:
154+
155+
![Socket DFA](./images/socket_dfa.png)
150156

151157
> Open [SocketExample.java](./src/main/java/com/tutorial/part3/SocketExample.java).
152158
@@ -159,7 +165,9 @@ Let's refine another external class.
159165

160166
> Open [ReentrantLockRefinements.java](./src/main/java/com/tutorial/part3/exercise/ReentrantLockRefinements.java). Your task is to replace the `"true"` refinements with the appropriate ones to refine the `ReentrantLock` class.
161167
162-
We want to ensure that the `lock` method can only be called in the `unlocked` state, and that the `unlock` method can only be called in the `locked` state.
168+
We want to ensure that the `lock` method can only be called in the `unlocked` state, and that the `unlock` method can only be called in the `locked` state. These transitions are represented by the following DFA:
169+
170+
![ReentrantLock DFA](./images/reentrant_lock_dfa.png)
163171

164172
With the correct implementation, LiquidJava will report an error in line 10 of [ReentrantLockExample.java](./src/main/java/com/tutorial/part3/exercise/ReentrantLockExample.java), since we are trying to unlock a lock that is not locked.
165173

@@ -174,6 +182,8 @@ Here, we define the refinements for the `ArrayList` class, using a ghost variabl
174182

175183
In the constructor, we specify that after it is called, the ghost variable `size` will be equal to `0`. This is optional since its default value is already zero, but it helps us understand this example. Then, in the `add` method, we specify that it can be called in any state (since we don't specify a `from` state), and that after it is called, the `size` ghost variable will be incremented by one — the new size will be equal to the old size plus one (`old` is a special keyword that refers to the previous state of the object, so calling `size(old(this))` gets the value of `size` before the method was called). Finally, in the `get` method, we specify that the index parameter must be non-negative and less than the current size of the list, therefore preventing out-of-bounds errors.
176184

185+
The state transitions are represented by the following DFA:
186+
177187
> Open [ArrayListExample.java](./src/main/java/com/tutorial/part4/ArrayListExample.java).
178188
179189
Here, we can see a simple usage of the refined `ArrayList` class. If you uncomment line 11, LiquidJava will report an error, since we are trying to access an index that is out of bounds!

images/light_bulb_dfa.png

24.1 KB
Loading

images/media_player_dfa.png

67.9 KB
Loading

images/reentrant_lock_dfa.png

44.5 KB
Loading

images/socket_dfa.png

102 KB
Loading

src/main/java/com/tutorial/part3/SocketRefinements.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface SocketRefinements {
1212
@StateRefinement(to="unconnected(this)")
1313
public void Socket();
1414

15-
@StateRefinement(from="unconnected(this)",to="bound(this)")
15+
@StateRefinement(from="unconnected(this)", to="bound(this)")
1616
public void bind(SocketAddress add);
1717

1818
@StateRefinement(from="bound(this)", to="connected(this)")

0 commit comments

Comments
 (0)