| # FIDL bindings specification |
| |
| This document is a specification of Fuchsia Interface Definition Language |
| (**FIDL**) bindings. It is meant to provide guidance and best practices for |
| bindings authors, and recommend specific approaches for their ergonomic use. |
| |
| In this document, the following keywords are to be interpreted as described in |
| [RFC2119][RFC2119]: **MAY**, **MUST**, **MUST NOT**, **OPTIONAL**, |
| **RECOMMENDED**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD |
| NOT**. |
| |
| ## Generated code indication |
| |
| A comment must be placed at the top of machine-generated code to indicate it is |
| machine generated. For languages with a standard on how to indicate generated |
| sources (as opposed to human-written code), that standard must be followed. |
| |
| In [Go][go-generated-code-comment] for instance, generated sources must be |
| marked with a comment following the pattern |
| |
| ```go |
| // Code generated by <tool>; DO NOT EDIT. |
| ``` |
| |
| ## Scoping |
| |
| It is RECOMMENDED to namespace machine-generated code to avoid clashing with |
| user-defined symbols. This can be implement using scoping constructs provided by |
| the language, like namespaces in C++, modules in Rust, or packages in Go and |
| Dart. If the generated scope can have a name, it SHOULD be named using |
| components of the FIDL library name which contains the definitions for the |
| generated code, which allows each FIDL library to exist in a unique scope. In |
| cases where scoping is not possible and the namespace is shared, some processing |
| of the generated names (see [Naming](#naming)) may be necessary. |
| |
| ## Naming {#naming} |
| |
| In general, the names used in the generated code SHOULD match the names used in |
| the FIDL definition. Possible exceptions are listed in the following sections. |
| |
| ### Casing |
| |
| Casing changes SHOULD be made to fit the idiomatic style of the language (e.g. |
| using snake_case or CamelCase). `fidlc` will ensure that identifier uniqueness |
| is enforced taking into account potential casing differences (see [FTP 40][ftp040]). |
| |
| ### Reserved keywords and name clashes |
| |
| The generated code MUST take into account the reserved keywords in the target |
| language to avoid unexpected when a keyword from the target language is used in |
| the FIDL definition. An example scheme would be to prefix conflicting names with |
| an underscore `_` (assuming no keywords begin with an underscore). |
| |
| The generated code MUST avoid generating code that causes naming conflicts. For |
| example, in a function whose parameters are generated based on a FIDL |
| definition, it MUST be impossible for the names of the local variables in the |
| generated to clash with possible generated names. |
| |
| ## Ordinals |
| |
| ### Method ordinals |
| |
| Ordinals used for methods are large 64-bit numbers. Bindings SHOULD emit these |
| ordinals in hexadecimal, i.e. `0x60e700e002995ef8`, not `6982550709377523448`. |
| |
| ### Union, and table ordinals |
| |
| Ordinals used for `union` and `table` start at 1, and must form a dense space. |
| Therefore, these numbers are typically small, and bindings SHOULD emit these |
| ordinals in decimal notation. |
| |
| ## Native types |
| |
| It is RECOMMENDED that bindings use the most specific and ergonomic native types |
| where possible when converting built-in FIDL types to native types in the target |
| language. For example, the Dart bindings use `Int32List` to represent a |
| `vector<int32>:N` and `array<int32>:N` rather than the more generic `List<int>`. |
| |
| ## Generated types and values |
| |
| ### Constant support |
| |
| Generated code MUST generate variables containing matching values for each |
| `const` definition in the corresponding FIDL. These variables SHOULD be marked |
| as immutable in languages that support this (e.g. `const` in C++, Rust, and Go, |
| or `final` in Dart). |
| |
| ### Bits support |
| |
| Bindings MUST provide generated values for each bits member. They MAY also |
| generate values representing the bits with no flags set, as well as the bits |
| with every flag set (the "bits mask"). These values SHOULD be scoped to each set |
| of bits. |
| |
| It is RECOMMENDED to support the following operators over generated values: |
| |
| * bitwise and, i.e `&` |
| * bitwise or, i.e `|` |
| * bitwise exclusive-or, i.e `^` |
| * bitwise not, i.e `~` |
| |
| To provide bitwise operations which always result in valid bits values, |
| implementations of bitwise not should further mask the resulting value with the |
| mask of all values. In pseudo code: |
| |
| ``` |
| ~value1 means mask & ~bits_of(value1) |
| ``` |
| |
| This mask value is provided in the [JSON IR][jsonir] for convenience. |
| |
| Bindings SHOULD NOT support other operators since they could result in invalid |
| bits value (or risk a non-obvious translation of their meaning), e.g.: |
| |
| * bitwise shifts, i.e `<<` or `>>` |
| * bitwise unsigned shift, i.e `>>>` |
| |
| For cases where the generated code includes a type wrapping the underlying |
| numeric bits value, it SHOULD be possible to convert between the raw value and |
| the wrapper type. It is RECOMMENDED for this conversion to be explicit. |
| |
| ### Enum support |
| |
| Bindings MUST provide generated values for each enum member. These values SHOULD |
| be scoped to each enum. |
| |
| For cases where the generated code includes a type wrapping the underlying |
| numeric enum value, it SHOULD be possible to convert between the raw value and |
| the wrapper type. It is RECOMMENDED for this conversion to be explicit. |
| |
| ### Struct support |
| |
| Bindings MUST provide a type for each struct that supports the following |
| operations: |
| |
| * Construction with explicit values for each member. |
| * Reading and writing members. |
| |
| Bindings MAY support default values for structs. The default values are |
| specified in the [JSON IR][jsonir]. |
| |
| ### Union support |
| |
| Bindings MUST provide a type for each union that supports the following |
| operations: |
| |
| * Construction with an explicit variant set. It is NOT RECOMMENDED for bindings |
| to offer construction without a variant. This should be considered only for |
| performance reasons or due to limitations of the target language. |
| * Reading/writing the variant of the union and the data associated with that |
| variant. |
| |
| For languages without union types or union value literals, it is RECOMMENDED to |
| support factory methods for constructing new unions given a value for one of the |
| possible variants. For example, in a C like language, this would allow replacing |
| code like: |
| |
| ```C |
| my_union_t foo; |
| foo.set_variant(bar); |
| do_stuff(foo); |
| ``` |
| |
| with something like: |
| |
| ```C |
| do_stuff(my_union_with_variant(bar)); |
| ``` |
| |
| These factory methods SHOULD be named as "[Type]-with-[Variant]", cased properly |
| for the target language. |
| |
| Examples of this exist for the |
| [HLCPP](https://fuchsia-review.googlesource.com/c/fuchsia/+/309246/) and |
| [Go](https://fuchsia-review.googlesource.com/c/fuchsia/+/313205/) bindings. |
| |
| #### Flexible unions |
| |
| The bindings MUST succeed when decoding a flexible union with an unknown |
| variant. The behavior of such a union can vary. Bindings MAY provide ways for |
| the user to read the underlying raw bytes and handles of the payload, as well as |
| the unknown ordinal. Bindings SHOULD either provide access to both bytes and |
| handles, or neither. |
| |
| For bindings that support storing accessing the unknown bytes, handles, and |
| ordinals: |
| |
| * Bindings MAY provide a constructor to create a union with an unknown variant |
| with specified ordinal, bytes, and handles. |
| * Such a constructor is useful not just for testing the bindings, but also for |
| end-developer testing needs (e.g. to check that unknown data is handled |
| correctly in a proxy). |
| * Having a constructor also prevents end-developers from constructing unions |
| with unknown variants in roundabout ways, such as by manually decoding raw |
| bytes. |
| * Usage of this constructor is discouraged in production code. |
| * Bindings SHOULD support re-encoding the union, writing the unknown ordinal, |
| bytes, and handles back onto the wire. |
| |
| For bindings that do not store the unknown bytes, handles, and ordinal: |
| |
| * Bindings SHOULD fail to encode rather than send a message with missing data. |
| |
| ### Table support |
| |
| Bindings MUST provide a type for each table that supports the following |
| operations: |
| |
| * Construction where specifying values for each member is optional. |
| * Reading and writing each member, including checking whether a given member is |
| set. These SHOULD follow the naming scheme: `get_[member]`, `set_[member]`, |
| and `has_[member]`, cased properly for the target language. |
| |
| Bindings MAY support default values for tables. The default values are specified |
| in the [JSON IR][jsonir]. |
| |
| Bindings MAY provide constructors for tables that only require specifying values |
| for fields that have a value. For example, in Rust this can be accomplished |
| using the `::empty()` constructor along with struct update syntax. Supporting |
| construction this ways allows users to write code that is robust against |
| addition of new fields to the table. |
| |
| ## Protocol support |
| |
| ### Error types |
| |
| It is OPTIONAL that bindings provide some form of special support for protocol |
| methods with an error type matching the idiomatic way errors are handled in the |
| target language. |
| |
| For example, languages that provide some form of a "result" type (i.e. a union |
| type that contains a "success" variant and an "error" variant), such as Rust's |
| `result::Result`, or `fit::result` in C++ MAY provide automatic conversions to |
| and from these types when receiving or sending method responses with an error |
| type. |
| |
| Languages with exceptions can have the generated protocol method code optionally |
| raise an exception corresponding to the error type. |
| |
| In cases where this is not possible, the generated code MAY provide convenience |
| functions for responding directly with a successful response or error value, or |
| for receiving an error type response, in order avoid boilerplate user code for |
| initializing result unions. |
| |
| ## Error handling |
| |
| Protocols MAY surface transport errors back to the user. Transport errors can be |
| categorized as errors encountered when converting between the native type and |
| the wire format data, or as errors from the underlying transport mechanism (for |
| example, an error obtained from calling `zx_channel_write`). These errors MAY |
| consist of the error status, as well as any other diagnostics information. |
| |
| ### Attributes |
| |
| Bindings MUST support the following [attributes][attributes]: |
| |
| * `[Transitional]` |
| |
| ## Best practices |
| |
| ### Alternative output |
| |
| It is OPTIONAL for bindings to provide alternative output methods to the FIDL |
| wire format. |
| |
| One type of output could be user-friendly debug printing for the generated |
| types. For example, printing a value of the bits: |
| |
| ```fidl |
| {%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/misc.test.fidl" region_tag="mode" %} |
| ``` |
| |
| could print the string `"Mode.Read | Mode.Write"` rather than the raw value |
| `"0b11"`. |
| |
| Similar user-friendly printing can be implemented for each of the generated FIDL |
| types. |
| |
| Another example of alternative output would be serializing FIDL values to JSON. |
| Users SHOULD have the option to opt-in or out to this functionality in order to |
| follow the principle of "only pay for what you use". An example of this is |
| `dart_fidl_json`, which is implemented using `fidlmerge`. |
| |
| ### Message memory allocation |
| |
| Bindings MAY provide the option for users to provide their own memory to use |
| when sending or receiving messages, which allows the user to control memory |
| allocation. |
| |
| ### Wire format memory layout |
| |
| Bindings MAY have the in memory layout of the generated FIDL types match the |
| wire format of the type. Doing this can in theory avoid extra copies, as the |
| data can be used directly as the transactional message, or vice versa. In |
| practice, sending a FIDL message may still involve a copying step where the |
| components of a message are assembled into a contiguous chunk of memory (called |
| "linearization"). The downside of such an approach is that it makes the bindings |
| more rigid: changes to the FIDL wire format become more complex to implement. |
| |
| The [LLCPP bindings][llcpp] are the only binding which take this |
| approach. |
| |
| ### Equality comparison |
| |
| For aggregate types such as structs, tables, and unions, bindings MAY provide |
| equality operators that perform a deep comparison on two instances of the same |
| type. These operators SHOULD NOT be provided for resource types (see FTP-057) as |
| comparison of handles is not possible. Avoiding exposing equality operators for |
| resource types prevents source breakages caused by an equality operation |
| 'disappearing' when a handle is added to the type. |
| |
| ### Copying |
| |
| For aggregate types such as structs, tables, and unions, bindings MAY provide |
| functionality for copying instances of these types. Copying SHOULD NOT be |
| provided for resource types (see [FTP-057][ftp057]) as making copies of handles |
| is not guaranteed to succeed. Avoiding exposing copy operators for resource |
| types prevents source breakages caused by a copy operation 'disappearing' or |
| having its signature change when a handle is added to the type. |
| |
| ### Test utilities |
| |
| It is OPTIONAL for bindings to generate additional code specifically to be used |
| during testing. For example, the bindings can generate stub implementations of |
| each protocol so that users only need too verride specific methods that are |
| going to be exercised in a test. |
| |
| ### Epitaphs |
| |
| Bindings SHOULD provide support for epitaphs, i.e. generated code that allows |
| servers to send epitaphs and clients to receive and handle epitaphs. |
| |
| ### Setters and Getters |
| |
| Bindings MAY provide setters and getters for fields on aggregate types (structs, |
| unions, and tables). Even in languages where getter/setter methods are |
| un-idiomatic, using these methods will allow renaming internal field names |
| without breaking usages of that field. |
| |
| ### Request "responders" |
| |
| When implementing a FIDL protocol using the FIDL bindings in a target language, |
| the bindings provide an API to read the request parameters, and a way to write |
| the response parameters, if any. For example, the request parameters could be |
| provided as the arguments to a function, and the response parameters could be |
| provided as the return type of the function. |
| |
| For a FIDL protocol: |
| |
| ```fidl |
| {%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/misc.test.fidl" region_tag="hasher" %} |
| ``` |
| |
| A binding might generate: |
| |
| ```typescript |
| // Users would implement this interface to provide an implementation of the |
| // Hasher FIDL protocol |
| interface Hasher { |
| // Respond to the request by returning the desired response |
| hash: (value: string): Uint8Array; |
| }; |
| ``` |
| |
| Bindings MAY provide a responder object that is used to write |
| responses. In the example above, this would mean passing an additional |
| responder object in the function arguments, and having the function return void: |
| |
| ```typescript |
| interface Hasher { |
| hash: (value: string, responder: HashResponder): void; |
| }; |
| |
| interface HashResponder { |
| sendResponse(value: Uint8Array); |
| }; |
| ``` |
| |
| The use of a responder object has the following benefits: |
| |
| * Improved ergonomics: responders can be used to provide any type of |
| interaction with the client. For example, responders can have methods that |
| close the channel with an epitaph, or provide APIs for sending events. For |
| two-way methods, the responder could provide the mechanism to send a response. |
| * Increased flexibility: encapsulating all these behaviors in a single type |
| makes it possible to add or remove behavior from the bindings without |
| making breaking changes to bindings users, by only changing the responder |
| object, and not the protocol object. |
| |
| When providing a responder object, bindings should be careful about responders |
| being invoked on a different thread than the one the request was processed on. |
| Responders may also be invoked much later than the request was processed, for |
| instance when implement a handing get pattern. In practice this could be |
| implemented by allowing users to move ownership of the responder out of the |
| request handler class, e.g. into a callback for an asynchronous function. |
| |
| The object MAY NOT necessarily be called responder. For example, it could have |
| a different name depending on whether the method is fire and forget or two way: |
| |
| ```typescript |
| interface Hasher { |
| // the Hash method is a two-way method, so the object is called a responder |
| hash: (value: string, responder: HashResponder): void; |
| // the SetSeed method is a fire and forget method, so it gets a different name |
| setSeed: (seed: number, control: HasherControlHandle): void; |
| } |
| ``` |
| |
| ## Related Documents |
| |
| * [FTP-024: Mandatory Source Compatibility][ftp024] |
| |
| <!-- xrefs --> |
| [jsonir]: /docs/reference/fidl/language/json-ir.md |
| [ftp024]: /docs/contribute/governance/fidl/ftp/ftp-024.md |
| [ftp040]: /docs/contribute/governance/fidl/ftp/ftp-040.md |
| [ftp057]: /docs/contribute/governance/fidl/ftp/ftp-057.md |
| [RFC2119]: https://tools.ietf.org/html/rfc2119 |
| [go-generated-code-comment]: https://github.com/golang/go/issues/13560#issuecomment-288457920 |
| [attributes]: /docs/reference/fidl/language/attributes.md |
| [llcpp]: /docs/reference/fidl/bindings/llcpp-bindings.md |