The Debug Adapter Protocol (henceforth DAP) describes a general method and message protocol for building debuggers. A DAP frontend will send Request JSON messages to the DAP adapter which then performs that request action and responds with a Response JSON message, and optionally one or more Event JSON messages.
Microsoft publishes a machine-readable schema describing all the message types and supporting objects. This schema file is also checked in to the weevil codebase.
So the first thing a new adapter implementation needs is a way to consume and produce JSON messages of these types.
Given that there are so many message types and also that the schema doesn't change frequently we opted to auto-generate OCaml code to handle this.
The dap-gen CLI tool is used to re-generate the necessary OCaml files:
$ dune exec -- weevil dap-gen --helpSee below for more details.
As noted above, there are three message types: Request, Response and Event. Requests and Responses have a related command value and Events have an event value. All message types also carry some sort of data, and this can be required or optional.
The command and event values are basically enumerations and the data content types are typically JSON objects that themselves contain other objects/lists/enums etc.
This leads to the following OCaml representation (c.f. requests & responses & events ):
type ('cmd, 'args, 'presence) Dap_request_message.t
type ('cmd, 'body, 'presence) Dap_response_message.t
type ('event, 'body, 'presence) Dap_event_message.t
Here 'cmd and 'event relate to the command and event enums described previously. The 'args and 'body parameters are the contents of the message, and the 'presence parameter determines whether this content is required or optional.
On processing the JSON schema the auto-generation tool can lock in the correct types of each message kind thus eliminating all kinds of bugs that could arise due to incorrect message construction. For example
( Dap_events.stopped, StoppedEvent_body.t, Presence.req ) Dap_event_message.t
is a stopped event message that has some required content data of type StoppedEvent_body.t,
where-as
( Dap_commands.initialize, Capabilities.t option, Presence.opt ) Dap_response_message.t
is an initialize response message that has optional content data of type Capabilities.t.
Editor tooling like Merlin and Tuareg also helps tremendously as each OCaml message type knows what contents it needs.
The dap-gen tool will generate the command and event enumerations (c.f. commands/impl and events/impl and also the message types with supporting objects).
Simply run it with the required output type, JSON schema and filename:
$ dune exec -- weevil dap-gen messages ./schema/debugAdapterProtocol-1.56.X.json $HOME/dap_messagewill output the dap_message.ml file in your $HOME dir. Similarly,
$ dune exec -- weevil dap-gen commands ./schema/debugAdapterProtocol-1.56.X.json $HOME/dap_commandsWill generate dap_commands.ml and dap_commands.mli in $HOME, and
$ dune exec -- weevil dap-gen events ./schema/debugAdapterProtocol-1.56.X.json $HOME/dap_eventswill generate dap_events.ml and dap_events.mli in $HOME.
If you are happy with the generated code files then move/copy the files into the dapper library
$ cp dap_*.ml* ./lib/dapper/NOTE The dapper library is expecting these files to be named as above.
The Weevil uses the same JSON encoder lib as Octez, namely data-encoding.
All generated message types and object types have a similar structure:
- they all have a constructor called
makethat takes named parameters for construction. Named parameters are required/optional as stated in the JSON schema using the standard OCaml syntax (~and?) e.g.
let make ~a_required_parameter ?an_optional_parameter ... () =
...etc -
Request/Response/Event types also have a
make_optconstructor for creating message types with optional content data, -
due to the use of named parameters everywhere all of these constructors have to end in () i.e. unit - this is an OCaml artifact related to named parameters and currying,
-
they all have a
Data_encoding.tencoder namedenc, -
Request/Response/Event types also have an
enc_optencoder to encode/decode message types with optional content data, -
they all have (fairly simple) qcheck generators for property based testing