Skip to content

Draw a @StateRefinement without from as an entry arrow, not an edge from every state #14

Closed

Description

@CatarinaGamboa

What happens

StateMachineParser.getTransitions (StateMachineParser.java:447) expands a @StateRefinement that has no from into one edge from every state:

List<TransitionSource> fromSources = from.isEmpty() ? allStateSources(states) : parsePrecondition(from, states);

Example: an UndoManager spec where canUndo() works as a guard:

@StateSet({"start", "canEdit", "endDone", "endUndone"})
...
@StateRefinement(from="start(this)", to="!return && start(this)")
@StateRefinement(from="canEdit(this)")
@StateRefinement(from="endDone(this)")
@StateRefinement(from="endUndone(this)", to="!return && endUndone(this)")
@StateRefinement(to="return ? endDone(this) : true")
boolean canUndo();

The machine gets four canUndo [return] edges into endDone: from start, canEdit, endDone and endUndone.

Why that is misleading

When the state is known, the verifier does not follow these edges. The matching from= alternative decides. Checked with verifier 0.0.36: addEdit(e); if (um.canUndo()) um.addEdit(e2); verifies, so canEdit does not move to endDone. Two of the drawn edges also contradict their own state's alternative (start and endUndone both say !return).

The from-less refinement only matters when the receiver's state is unknown: typestate is tracked within one method, so this happens with a field, a parameter, or the result of a getter (frame.getUndoManager().undo()). There it is how the object enters the protocol: if (um.canUndo()) um.undo(); verifies because a true answer puts um in endDone.

Proposal

Draw a from-less @StateRefinement as an extra entry arrow from [*], like a constructor's initial transition, labelled with the method and its condition:

stateDiagram-v2
    [*] --> start: new UndoManager()
    [*] --> endDone: canUndo() / canUndoOrRedo() is true
    [*] --> endUndone: canRedo() is true
    start --> canEdit: addEdit / undoableEditHappened
    start --> endDone: end
    canEdit --> endDone: end
    endDone --> endUndone: undo / undoOrRedo
    endUndone --> endDone: redo
Loading

For example, add them to initialTransitions with the method as label and the to condition (return), or add a separate entryTransitions list so that clients (the MCP's get_state_machine, the VS Code extension) can render them differently from constructors.

Optionally, when a from-less refinement sits beside from= alternatives, an edge it would add from a state that already has its own alternative could be dropped, since that alternative decides in a known state.

Context: found while building study exercises (UndoManager spec in the building-examples-liquidjava specs-v2 overlay).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions