| # FIDL Overview |
| |
| This document is a high level overview of the Fuchsia Interface Definition |
| Language (FIDL), which is the language used to describe interprocess |
| communication (IPC) protocols used by programs running on Fuchsia. This overview |
| introduces the concepts behind FIDL — developers familiar with these concepts |
| already can start writing code by following the [tutorials][fidl-tutorials], or |
| dive deeper by reading the [language][language-reference] or |
| [bindings][bindings-reference] references. |
| |
| ## What is FIDL? |
| |
| While "**FIDL**" stands for "Fuchsia Interface Definition Language," the word |
| itself can be used to refer to a number of different concepts: |
| |
| * [**FIDL wire format**][wire-format]: the FIDL wire format specifies how FIDL |
| messages are represented in memory for transmission over IPC |
| * [**FIDL language**][language-spec]: the FIDL language is the syntax by which |
| protocols are described in `.fidl` files |
| * [**FIDL compiler**][compiler-spec]: the FIDL compiler generates code for |
| programs to use and implement protocols |
| * [**FIDL bindings**][bindings-reference]: the FIDL bindings are |
| language-specific runtime support libraries and code generators that provide |
| APIs for manipulating FIDL data structures and protocols. |
| |
| The main job of FIDL is to allow diverse clients and services to interoperate. |
| Client diversity is aided by decoupling the implementation of the IPC mechanism |
| from its definition, and is simplified by automatic code generation. |
| |
| The FIDL language provides a familiar (though simplified) C-like declaration |
| syntax that allows the service provider to exactly define their protocols. Basic |
| data types, like integers, floats, and strings, can be organized into more |
| complex aggregate structures and unions. Fixed arrays and dynamically sized |
| vectors can be constructed from both the basic types and the aggregate types, |
| and these can all be combined into even more complex data structures. |
| |
| Due to the number of client implementation target languages (C, C++, Rust, Dart, |
| and so on), we don't want to burden the developer of the service with providing |
| a protocol implementation for each and every one. |
| |
| This is where the FIDL toolchain comes in. The developer of the service creates |
| just one `.fidl` definition file, which defines the protocol. Using this file, |
| the FIDL compiler then generates client and server code in any of the supported |
| target languages. |
| |
| ![Figure: server written in C++ talks to clients written in multiple |
| languages](images/fidl_architecture.png) |
| |
| In many cases, there will only be one implementation of the server (for example, |
| the particular service might be implemented in C++), whereas there could be any |
| number of implementations of the client, in a multitude of languages. |
| |
| > Note that the Fuchsia operating system has no innate knowledge of FIDL. The |
| > FIDL bindings use a standard channel communication mechanism in Fuchsia. The |
| > FIDL bindings and libraries enforce a set of semantic behavior and persistence |
| > formats on how that channel is used. |
| |
| ### FIDL architecture |
| |
| From a developer's point of view, the following are the main components: |
| |
| * FIDL definition file — this is a text file (ending in `.fidl` by |
| convention) that defines the values, and protocols (methods with their |
| parameters), |
| * client code — generated by the FIDL compiler (`fidlc`) toolchain for |
| each specific target language, and |
| * server code — also generated by the FIDL compiler toolchain. |
| |
| As a very simple example of a FIDL definition file, consider an "echo" service |
| — whatever the client sends to the server, the server just echoes back to |
| the client. |
| |
| > Line numbers have been added for clarity and are not part of the `.fidl` file. |
| |
| ```fidl |
| 1 library fidl.examples.echo; |
| 2 |
| 3 @discoverable |
| 4 protocol Echo { |
| 5 EchoString(struct { |
| 6 value string:optional; |
| 7 }) -> (struct { |
| 8 response string:optional; |
| 9 }); |
| 10 }; |
| ``` |
| |
| Let's go through it line by line. |
| |
| **Line 1:** The `library` keyword is used to define a namespace for this |
| protocol. FIDL protocols in different libraries might have the same name, so the |
| namespace is used to distinguish amongst them. |
| |
| **Line 3:** The `@discoverable` [**attribute**][attributes] indicates that the |
| protocol that follows should be made available for clients to connect to. |
| |
| **Line 4:** The `protocol` keyword introduces the name of the protocol, here |
| it's called `Echo`. |
| |
| **Lines 5-9:** The method, its parameters, and return values. There are two unusual |
| aspects of this line: |
| |
| * Note the declaration `string:optional` (for both `value` and `response`). The |
| `string` part indicates that the parameters are strings (sequences of |
| characters), while the `optional` constraint indicates that the parameter is |
| optional. |
| * The parameters are wrapped in a `struct`, which is the top level type |
| containing the method parameters. |
| * The `->` part indicates the return, which appears after the method |
| declaration, not before. Unlike C++ or Java, a method can return multiple |
| values. |
| |
| The above FIDL file, then, has declared one protocol, called `Echo`, with one |
| method, called `EchoString`, that takes a nullable string and returns a nullable |
| string. |
| |
| The simple example above used just one data type, the `string` as both the input |
| to the method as well as the output. |
| |
| The possible FIDL data types are very flexible: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples.docs/misc.test.fidl" region_tag="example-struct" %} |
| ``` |
| |
| The above declares a structure called `MyRequest` with three members: an |
| unsigned 32-bit integer called `serial`, a string called `key`, and a vector of |
| unsigned 32-bit integers called `options` |
| |
| ### Messaging Models |
| |
| In order to understand FIDL's messaging, we need to break things up into two |
| layers, and clarify some definitions. |
| |
| At the bottom (the operating system layer), there's an asynchronous |
| communications scheme geared towards independent progress of a **sender** and a |
| **receiver**: |
| |
| * **sender** — the party that originates a message, |
| * **receiver** — the party that receives a message, |
| |
| Sending a message is a non-blocking operation: the sender sends the message, and |
| is then free to continue processing, regardless of what the receiver is doing. |
| |
| A receiver can, if it wants to, block in order to wait for a message. |
| |
| The top layer implements FIDL messages, and uses the bottom (asynchronous) |
| layer. It deals with **client**s and **server**s: |
| |
| * **client** — the party that is making a request (of a server), |
| * **server** — the party that is processing a request (on behalf of a |
| client). |
| |
| > The terms "sender" and "receiver" make sense when we're discussing the |
| > messages themselves — the underlying communications scheme isn't |
| > concerned about the roles that we've assigned to the parties, just that one is |
| > sending and one is receiving. |
| > |
| > The terms "client" and "server" make sense when we're discussing the roles |
| > that the parties play. In particular, a client can be a sender at one time, |
| > and a receiver at a different time; same for the server. |
| |
| Practically speaking, in the context of a client / server interaction, that |
| means that there are several models: |
| |
| 1. **blocking call** — client sends to server, waits for reply |
| 2. **fire and forget** — client sends to server, doesn't expect reply |
| 3. **callback** or **async call** — client sends to server, but doesn't |
| block; a reply is delivered asynchronously some time later |
| 4. **event** — server sends to client, without the client having asked |
| for data |
| |
| The first is synchronous, the rest are asynchronous. We'll discuss these in |
| order. |
| |
| #### Client sends to server, waits for a reply |
| |
| This model is the traditional "blocking call" or "function call" available in |
| most programming languages, except that the invocation is done over a channel, |
| and thus can fail due to transport level errors. |
| |
| From the point of view of the client, it consists of a call that blocks, while |
| the server performs some processing. |
| |
| ![Figure: client and server](images/blocking.png) |
| |
| Here's a step-by-step description: |
| |
| 1. A client makes a call (optionally containing data) and blocks. |
| 2. The server receives the client's call (and optional data), and performs some |
| amount of processing. |
| 3. At the server's discretion, it replies to the client (with optional data). |
| 4. The server's reply causes the client to unblock. |
| |
| To implement this synchronous messaging model over an asynchronous messaging |
| scheme is simple. Recall that both the client-to-server and server-to-client |
| message transfers are, at the bottom layer in the protocol, asynchronous. The |
| synchronization happens at the client end, by having the client block until the |
| server's message arrives. |
| |
| Basically, in this model, the client and server have come to an agreement: |
| |
| * data flow is initiated by the client, |
| * the client shall have at most only one message outstanding, |
| * the server shall send a message to the client only in response to a client's |
| message |
| * the client shall wait for the server's response before continuing. |
| |
| This blocking model is commonly used where the client needs to get the reply to |
| its current request before it can continue. |
| |
| For example, the client may request data from the server, and not be able to do |
| any other useful processing until that data arrives. |
| |
| Or, the client may need to perform steps in a specific order, and must therefore |
| ensure that each step completes before initiating the next one. If an error |
| occurs, the client may need to perform corrective actions that depend on how far |
| the operation has proceeded — another reason to be synchronized to the |
| completion of each step. |
| |
| #### Client sends to server, no reply {#fire-and-forget} |
| |
| This model is also known as "fire and forget." In it, the client sends the |
| message to the server. and then carries on with its operation. In contrast to |
| the blocking model, the client *does not* block, *nor does it expect a |
| response*. |
| |
| This model is used in cases where the client doesn't need to (or cannot) |
| synchronize to the processing of its request. |
| |
| ![Figure: Fire and Forget; client sends to server but doesn't expect |
| replies](images/faf.png) |
| |
| The classic example is a logging system. The client sends logging information to |
| the logging server (circles "1" and "2" in the diagram above), but has no reason |
| to block. A lot of things can go wrong at the server end: |
| |
| 1. the server is busy and can't handle the write request at this moment, |
| 2. the media is full and the server can't write the data, |
| 3. the server has encountered a fault, |
| 4. and so on. |
| |
| However, the client isn't in a position to do anything about those problems, so |
| blocking would just create more problems. |
| |
| #### Client sends to server, but doesn't block |
| |
| This model, and the next one ("server sends to client, without client asking for |
| data") are similar. |
| |
| In the present model, the client sends a message to a server, but doesn't block. |
| However, the client expects some kind of response from the server, but the key |
| here is that it's not *synchronous* with the request. |
| |
| This allows great flexibility in the client / server interaction. |
| |
| While the synchronous model forces the client to wait until the server replies, |
| the present model frees the client to do something else while the server is |
| processing the request: |
| |
| ![Figure: client sends to server but doesn't block until later](images/async.png) |
| |
| The subtle difference in this diagram vs. the similar one above is that after |
| circle "1" the client is *still running*. The client chooses when to give up |
| CPU; it's not synchronous with the message. |
| |
| There are actually two sub-cases here — one in which the client gets just |
| one response, and another in which the client can get multiple responses. (The |
| one where the client gets zero responses is the "fire and forget" model, which |
| we discussed earlier.) |
| |
| ##### Single request, single response |
| |
| The single response case is the closest to the synchronous model: the client |
| sends a message, and eventually, the server replies. You'd use this model |
| instead of multi-threading, for example, when you know that the client could be |
| doing useful work while waiting for the server's reply. |
| |
| ##### Single request, multiple response |
| |
| The multiple response case can be used in a "subscription" model. The client's |
| message "primes" the server, for example, requesting notification whenever |
| something happens. |
| |
| The client then goes about its business. |
| |
| Some time later, the server notices that the condition that the client is |
| interested in has happened, and thus sends the client a message. From a client / |
| server point of view, this message is a "reply", with the client receiving it |
| asynchronously to its request. |
| |
| ![Figure: client sends to server, server replies multiple |
| times](images/async-multiple.png) |
| |
| There's no reason why the server couldn't send another message when another |
| event of interest occurs; this is the "multiple response" version of the model. |
| Note that the second (and subsequent) responses are sent *without* the client |
| sending any additional messages. |
| |
| > Note that the client doesn't *need* to wait for the server to send it a |
| > message. In the diagram above, we showed the client in the blocked state |
| > before circle "3" — the client could just as well have been running. |
| |
| #### Server sends to client, without client asking for data |
| |
| This model is also known as the "event" model. |
| |
| ![Figure: unsolicited messages from a server to a client](images/event.png) |
| |
| In it, a client prepares to receive messages from a server, but doesn't know |
| when to expect them — the messages are not only asynchronous to the |
| client, but are also (from a client / server point of view) "unsolicited", in |
| that the client didn't explicitly request them (like it did in the previous |
| model, above). |
| |
| The client designates a function (the "event handling function") to be called |
| when messages arrive from the server, but otherwise continues about its |
| business. |
| |
| At the server's discretion (circles "1" and "2" in the diagram above), messages |
| are sent asynchronously to the client, and handled by the client's designated |
| function. |
| |
| Note that the client may already be running when a message is sent (as in circle |
| "1"), or the client may have nothing to do and be waiting for a message to be |
| sent (as in circle "2"). |
| |
| > It is not a requirement that the client be waiting for a message. |
| |
| #### Asynchronous messaging complexity |
| |
| Breaking up asynchronous messaging into the above (somewhat arbitrary) |
| categories is meant to show typical usage patterns, but isn't meant to be |
| exhaustive. |
| |
| In the most general case of asynchronous messaging, you have zero or more client |
| messages loosely associated with zero or more server replies. It's this "loose |
| association" that adds the complexity in terms of your design process. |
| |
| |
| ### IPC models in FIDL |
| |
| Now that we have an understanding of the IPC models and how they interact with |
| FIDL's asynchronous messaging, let's see how they're defined. |
| |
| We'll add the other models (fire and forget, and async call or event) to the protocol |
| definition file: |
| |
| |
| ```fidl |
| 1 library fidl.examples.echo; |
| 2 |
| 3 @discoverable |
| 4 protocol Echo { |
| 5 EchoString(struct { |
| 6 value string:optional; |
| 7 }) -> (struct { |
| 8 response string:optional; |
| 9 }); |
| 10 |
| 11 SendString(struct { value string:optional; }); |
| 12 |
| 13 ->ReceiveString(struct { response string:optional; }); |
| 14 }; |
| ``` |
| |
| **Lines 5-9** are the `EchoString` method that we discussed above — it's a |
| traditional function call message, where the client calls `EchoString` with an |
| optional string, and then blocks, waiting for the server to reply with another |
| optional string. |
| |
| **Line 11** is the `SendString` method. It does not have the `->` return |
| declaration — that makes it into a "fire and forget" model (send only), |
| because we've told the FIDL compiler that this particular method does not have a |
| return associated with it. |
| |
| > Note that it's not the lack of return **parameters**, but rather the lack of |
| > return **declaration** that's the key here — putting "`-> ()`" after |
| > `SendString` would change the meaning from declaring a fire-and-forget style |
| > method to declaring a function call style method that doesn't have any return |
| > arguments. |
| |
| **Line 13** is the `ReceiveString` method. It's a little different — it |
| doesn't have the method name in the first part, but rather it's given after the |
| `->` operator. This tells the FIDL compiler that this is an "async call" or |
| "event" model declaration. |
| |
| ### FIDL Bindings |
| |
| The FIDL toolchain takes in FIDL protocol and type definitions, like the |
| examples shown above, and generates code in each target language that can "speak" |
| these protocols. This generated code is referred to as the FIDL bindings, which |
| are available in various flavors depending on the language: |
| |
| * **Native bindings**: designed for highly sensitive contexts such as device |
| drivers and high-throughput servers, leverage in-place access, avoid memory |
| allocation, but may require somewhat more awareness of the constraints of |
| the protocol on the part of the developer. |
| * **Idiomatic bindings**: designed to be more developer-friendly by copying |
| data from the wire format into easier to use data types (such as heap-backed |
| strings or vectors), but correspondingly somewhat less efficient as a |
| result. |
| |
| Bindings offer several various ways of invoking protocol methods depending on |
| the language: |
| |
| * **Send/receive**: read or write messages directly to a channel, no built-in |
| wait loop (C) |
| * **Callback-based**: received messages are dispatched asynchronously as |
| callbacks on an event loop (C++, Dart) |
| * **Port-based**: received messages are delivered to a port or future (Rust) |
| * **Synchronous call**: waits for reply and return it (Go, C++ unit tests) |
| |
| Bindings provide some or all of the following principal operations: |
| |
| * **Encode**: in-place transform native data structures into the wire format |
| (coupled with validation) |
| * **Decode**: in-place transform wire format data into native data structures |
| (coupled with validation) |
| * **Copy/Move To Idiomatic Form**: copy contents of native data structures |
| into idiomatic data structures, handles are moved |
| * **Copy/Move To Native Form**: copy contents of idiomatic data structures |
| into native data structures, handles are moved |
| * **Clone**: copy native or idiomatic data structures (that do not contain |
| move-only types) |
| * **Call**: invoke protocol method |
| |
| #### Client implementation |
| |
| Regardless of the target language, the `fidlc` FIDL compiler generates client |
| code that has the following basic structure. |
| |
| The first part consists of the administration and background handling, and |
| consists of: |
| |
| 1. some means of connecting to the server is provided |
| 2. an asynchronous ("background") message handling loop is started |
| 3. async call style and event style methods, if any, are bound to the message |
| loop |
| |
| The second part consists of implementations of the traditional function call or |
| fire and forget style methods, as appropriate for the target language. |
| Generally speaking, this consists of: |
| |
| 1. creating a callable API and declarations |
| 2. generating code for each API that marshals the data from the call into a FIDL |
| formatted buffer suitable for transmission to the server |
| 3. generating code to transmit the data to the server |
| 4. in the case of function call style calls, generating code to: |
| 1. wait for the response from the server |
| 2. unmarshal the data from the FIDL formatted buffer, and |
| 3. return the data via the API function. |
| |
| Obviously, the exact steps may vary due to language implementation differences, |
| but that's the basic outline. |
| |
| #### Server implementation |
| |
| The `fidlc` FIDL compiler also generates server code for a given target |
| language. Just like the client code, this code has a common structure regardless |
| of the target language. The code: |
| |
| 1. creates an object that clients can connect to, |
| 2. starts a main processing loop, which: |
| 1. waits for messages |
| 2. processes messages by calling out to the implementation functions |
| 3. if specified, issues an asynchronous call back to the client to return |
| the output |
| |
| In the next chapters, we'll see the details of each language's implementation of |
| the client and server code. |
| |
| |
| ## Why Use FIDL? |
| |
| Fuchsia extensively relies on IPC since most functionality is implemented in |
| user space outside of the kernel, including privileged components such as device |
| drivers. Consequently the IPC mechanism must be efficient, deterministic, |
| robust, and easy to use: |
| |
| **IPC efficiency** pertains to the computational overhead required to generate, |
| transfer, and consume messages between processes. IPC will be involved in all |
| aspects of system operation so it must be efficient. The FIDL compiler must |
| generate tight code without excess indirection or hidden costs. It should be at |
| least as good as hand-rolled code would be where it matters most. |
| |
| **IPC determinism** pertains to the ability to perform transactions within a |
| known resource envelope. IPC will be used extensively by critical system |
| services such as filesystems, which serve many clients and must perform in |
| predictable ways. The FIDL wire format must offer strong static guarantees such |
| as ensuring that structure size and layout is invariant thereby alleviating the |
| need for dynamic memory allocation or complex validation rules. |
| |
| **IPC robustness** pertains to the need to consider IPC as an essential part of |
| the operating system's ABI. Maintaining binary stability is crucial. Mechanisms |
| for protocol evolution must be designed conservatively so as not to violate the |
| invariants of existing services and their clients, particularly when the need |
| for determinism is also considered. The FIDL bindings must perform effective, |
| lightweight, and strict validation. |
| |
| **IPC ease of use** pertains to the fact that IPC protocols are an essential |
| part of the operating system's API. It is important to provide good developer |
| ergonomics for accessing services via IPC. The FIDL code generator removes the |
| burden of writing IPC bindings by hand. Moreover, the FIDL code generator can |
| produce different bindings to suit the needs of different audiences and their |
| idioms. |
| |
| ### Goals |
| |
| FIDL is designed specifically to optimize for these for characteristics. In |
| particular, the design of FIDL aims to satisfy the following goals: |
| |
| #### Specificity |
| |
| * Describe data structures and protocols used by IPC on Zircon. |
| * Optimized for interprocess communication. Although FIDL is also used for |
| persisting to disk and for network transfer, its design is not optimized for |
| these secondary use cases. |
| * Efficiently transport messages consisting of data (bytes) and capabilities |
| (handles) over Zircon channels between processes running on the same device. |
| * Designed specifically to facilitate effective use of Zircon primitives. |
| Although FIDL is used on other platforms (e.g. via ffx), its design puts |
| Fuchsia first. |
| * Offers convenient APIs for creating, sending, receiving, and consuming |
| messages. |
| * Perform sufficient validation to maintain protocol invariants (but no more |
| than that). |
| |
| #### Efficiency |
| |
| * Just as efficient (speed and memory) as using hand-rolled data structures |
| would be. |
| * Wire format uses uncompressed native datatypes with little-endianness and |
| correct alignment to support in-place access of message contents. |
| * No dynamic memory allocation is required to produce or to consume messages |
| when their size is statically known or bounded. |
| * Explicitly handle ownership with move-only semantics. |
| * Data structure packing order is canonical, unambiguous, and has minimum |
| padding. |
| * Avoid back-patching pointers. |
| * Avoid expensive validation. |
| * Avoid calculations that may overflow. |
| * Leverage pipelining of protocol requests for asynchronous operation. |
| * Structures are fixed size; variable-size data is stored out-of-line. |
| * Structures are not self-described; FIDL files describe their contents. |
| * No versioning of structures, but protocols can be extended with new methods |
| for evolution. |
| |
| #### Ergonomics |
| |
| * Programming language bindings maintained by Fuchsia team: |
| * C, New C++, High-Level C++ (Old), Dart, Go, Rust |
| * Keeping in mind we might want to support other languages in the future, such |
| as: |
| * Java, JavaScript, etc. |
| * The bindings and generated code are available in native or idiomatic flavors |
| depending on the intended application. |
| * Use compile-time code generation to optimize message serialization, |
| deserialization, and validation. |
| * FIDL syntax is familiar, easily accessible, and programming language |
| agnostic. |
| * FIDL provides a library system to simplify deployment and use by other |
| developers. |
| * FIDL expresses the most common data types needed for system APIs; it does |
| not seek to provide a comprehensive one-to-one mapping of all types offered |
| by all programming languages. |
| |
| ## Workflow |
| |
| This section recaps the workflow of authors, publishers, and consumers of IPC |
| protocols described using FIDL. |
| |
| ### Authoring FIDL |
| |
| The author of a FIDL based protocol creates one or more ***.fidl files** to |
| describe their data structures, protocols, and methods. |
| |
| FIDL files are grouped into one or more **FIDL libraries** by the author. Each |
| library represents a group of logically related functionality with a unique |
| library name. FIDL files within the same library implicitly have access to all |
| other declarations within the same library. The order of declarations within the |
| FIDL files that make up a library is not significant. |
| |
| FIDL files of one library can access declarations within another FIDL library by |
| **importing** the other FIDL module. Importing other FIDL libraries makes their |
| symbols available for use thereby enabling the construction of protocols derived |
| from them. Imported symbols must be qualified by the library name or by an alias |
| to prevent namespace collisions. |
| |
| ### Publishing FIDL |
| |
| The publisher of a FIDL based protocol is responsible for making FIDL libraries |
| available to consumers. For example, the author may disseminate FIDL libraries |
| in a public source repository or distribute them as part of an SDK. |
| |
| Consumers need only point the FIDL compiler at the directory that contains the |
| FIDL files for a library (and its dependencies) to generate code for that |
| library. The precise details for how this is done will generally be addressed by |
| the consumer's build system. |
| |
| ### Consuming FIDL |
| |
| The consumer of a FIDL based protocol uses the FIDL compiler to generate code |
| suitable for use with their language runtime specific bindings. For certain |
| language runtimes, the consumer may have a choice of a few different flavors of |
| generated code all of which are interoperable at the wire format level but |
| perhaps not at the source level. |
| |
| In the Fuchsia world build environment, generating code from FIDL libraries will |
| be done automatically for all relevant languages by individual FIDL build |
| targets for each library. |
| |
| In the Fuchsia SDK environment, generating code from FIDL libraries will be done |
| as part of compiling the applications that use them. |
| |
| ## Getting Started |
| |
| If you'd like to learn more about using FIDL, the Guides section has a number of |
| [developer guides][developer-guides] and [tutorials][fidl-tutorials] that you |
| can try. If you are developing on Fuchsia and would like to learn about how to |
| use bindings for an existing FIDL API, you can refer to the [FIDL bindings |
| reference][bindings-reference]. Finally, if you would like to learn more about FIDL or |
| would like to contribute, check out the [FIDL language |
| reference][language-reference], or the [contributing doc][fidl-contributing]. |
| |
| <!-- xrefs --> |
| [fidl-tutorials]: /docs/development/languages/fidl/tutorials/overview.md |
| [language-reference]: /docs/reference/fidl/language/language.md |
| [bindings-reference]: /docs/reference/fidl/bindings/overview.md |
| [wire-format]: /docs/reference/fidl/language/wire-format |
| [language-spec]: /docs/reference/fidl/language/language.md |
| [developer-guides]: /docs/development/languages/fidl/ |
| [fidl-contributing]: /docs/contribute/contributing-to-fidl/ |
| [compiler-spec]: /docs/development/languages/fidl/guides/ |
| [attributes]: /docs/reference/fidl/language/attributes.md#discoverable |