blob: 45831fdad6043a1171334efbc5d3d638a03984b7 [file] [log] [blame] [view]
# Dart bindings
## Libraries {#libraries}
Given the library declaration:
```fidl
library fuchsia.examples;
```
The bindings code for this library is generated into a
`fidl_fuchsia_examples_async` dart library. The `fidl_` prefix and `_async`
suffix are hardcoded by the FIDL toolchain.
This code can then be imported using:
```dart
{%includecode gerrit_repo="fuchsia/topaz" gerrit_path="examples/fidl/fidl_packages/test/types_test.dart" region_tag="import" %}
```
## Constants {#constants}
All [constants][lang-constants] are generated as a `const`. For example, the
following constants:
```fidl
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="consts" %}
```
Are generated as:
```dart
const int BOARD_SIZE = 9;
const String NAME = "Tic-Tac-Toe";
```
The correspondence between FIDL primitive types and Dart types is outlined in
[built-in types](#builtins).
## Fields {#fields}
This section describes how the FIDL toolchain converts FIDL types to native
types in Dart. These types can appear as members in an aggregate type, as
parameters to a protocol method, or as the type contained in an event or method
response `Future`.
Nullable types do not have different generated types than their non-nullable
counterparts in Dart.
### Built-in types {#builtins}
The FIDL types are converted to Dart types based on the following table:
|FIDL Type|Dart Type|
|--- |--- |
|`bool`|`bool`|
|`int8`, `int16`, `int32`, `int64`, `uint8`, `uint16`, `uint32`, `uint64`|`int`|
|`float32`, `float64`|`double`|
|`array<int8>:N`, `vector<int8>:N`|`Int8List`|
|`array<int16>:N`, `vector<int16>:N`|`Int16List`|
|`array<int32>:N`, `vector<int32>:N`|`Int32List`|
|`array<int64>:N`, `vector<int64>:N`|`Int64List`|
|`array<uint8>:N`, `vector<uint8>:N`|`Uint8List`|
|`array<uint16>:N`, `vector<uint16>:N`|`Uint16List`|
|`array<uint32>:N`, `vector<uint32>:N`|`Uint32List`|
|`array<uint64>:N`, `vector<uint64>:N`|`Uint64List`|
|`array<float32>:N`, `vector<float32>:N`|`Float32List`|
|`array<float64>:N`, `vector<float64>:N`|`Float64List`|
|`array<T>:N`, `vector<T>:N`|`List<T>`|
|`string`|`String`|
|`request<P>`|`fidl.InterfaceRequest<P>`|
|`P`|`fidl.InterfaceHandle<P>`|
|`handle:CHANNEL`|`zircon.Channel`|
|`handle:EVENTPAIR`|`zircon.EventPair`|
|`handle:SOCKET`|`zircon.Socket`|
|`handle:VMO`|`zircon.Vmo`|
|`handle:S`, `handle`|`zircon.Handle`|
### Response and event parameters {#response-event-parameters}
Method response and event types (see [Protocols](#protocols)) are represented
using `Future<T>`, where `T` is a type containing all of the response/event
parameters. This section describes how the FIDL toolchain generates this inner
type `T`.
* Empty responses and events use `void`.
* Responses and events with a single parameter `T` just use `T` as the response
or event type.
* Responses and events with multiple parameters use a generated wrapper class
which follows the naming scheme `[Protocol]$[Method]$Response`. For example,
an event `OnOpponentMove` for protocol `TicTacToe` that has multiple
parameters would use generated class `TicTacToe$OnOpponentMove$Response`. This
class provides a single method: the constructor, which has positional
arguments corresponding to the response or event parameters.
Note that methods that do not have a response will have a response type of
`Future<void>`, which is the same type used by methods with an empty response.
In the former case, the `Future` can be expected to resolve immediately after
sending the request, whereas in the latter case, the `Future` is only resolved
after receiving the empty response from the server.
## Type definitions {#type-definitions}
### Bits {#bits}
Given the [bits][lang-bits] definition:
```fidl
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="bits" %}
```
The FIDL toolchain generates a `FileMode` class with `static const` variables
for each bits member, as well as for a `FileMode` with no flags set:
* `static const FileMode read`
* `static const FileMode write`
* `static const FileMode execute`
* `static const FileMode $none`
`FileMode` provides the following methods:
* `int get $value`: Getter for the underlying int value.
* `String toString()`: Returns a readable representation of the `FileMode`.
* `FileMode operator |(FileMode other)`: Bitwise or operator.
* `FileMode operator &(FileMode other)`: Bitwise and operator.
* `bool operator(dynamic other)`: Equality operator.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/topaz" gerrit_path="examples/fidl/fidl_packages/test/types_test.dart" region_tag="bits" adjust_indentation="auto" %}
```
### Enums {#enums}
Given the [enum][lang-enums] definition:
```fidl
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="enums" %}
```
The FIDL toolchain generates a `LocationType` class with `static const` variables for
each enum member:
* `static const LocationType museum`
* `static const LocationType airport`
* `static const LocationType restaurant`
As well as the following variables:
* `static const Map<String, LocationType> $valuesMap`: A mapping ofthe string
representation of the member (`'museum'`, `'airport'`, or `'restaurant'`) to
its corresponding enum value (`LocationType.museum`, `LocationType.airport`,
or `LocationType.restaurant`)
* `static const List<LocationType> $values`: A list of all of the LocationTypes.
`LocationType` provides the following methods:
* `factory LocationType(int v)`: Factory constructor that returns the
corresponding `LocationType` static const variable (`museum`, `airport`, or
`restaurant`) if the input matches one of the discriminants, or throws
otherwise.
* `static LocationType $valueOf(String name)`: Look up a string name in the
`$valuesMap`.
* `String toString()`: Returns a readable representation of the `LocationType`.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/topaz" gerrit_path="examples/fidl/fidl_packages/test/types_test.dart" region_tag="enums" adjust_indentation="auto" %}
```
### Structs {#structs}
Given the [struct][lang-structs] declaration:
```fidl
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="structs" %}
```
The FIDL toolchain generates a `Color` class with the following methods:
* `const Color({@required id, name})`: The constructor for `Color` takes named
arguments corresponding to the `struct`'s fields. Fields that are not nullable
and do not have a default value specified are marked as `@required`.
* `int get id`: Getter for the `id` field.
* `String get name`: Getter for the `name` field.
* `Color.clone(Color, {int id, String name})`: Clone constructor that will clone
an existing `Color`, possibly overriding specific field values based on the
provided named arguments.
* `List<Object> get $fields`: Returns a list of fields in declaration order.
* `String toString()`: Returns a readable string of the `Color`
* `bool operator==(dynamic other)`: Equality operator that performs a deep
comparison when compared to another instance of a `Color`.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/topaz" gerrit_path="examples/fidl/fidl_packages/test/types_test.dart" region_tag="structs" adjust_indentation="auto" %}
```
### Unions {#unions}
Given the union definition:
```fidl
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="unions" %}
```
FIDL generates an `enum` representing the [tags][union-lexicon] of the union:
```dart
enum JsonValueTag {
intValue,
stringValue,
}
```
As well as a `JsonValue` class with the following methods:
* `const JsonValue.withIntValue(int)` and `const
JsonValue.withStringValue(String)`: Constructors for each variant.
* `JsonValueTag get $tag`: Getter for the tag corresponding to this the
[variant][union-lexicon] of this union.
* `int get intValue` and `String get stringValue`: Getter for the underlying
value. If the instance's variant does not match the getter method, `null` is
returned.
* `String toString()`: Returns a readable string of the `JsonValue`.
* `int get $ordinal`: Getter for the underlying [ordinal][union-lexicon] value.
* `Object get $data`: Getter for the underlying union data.
* `bool operator ==(dynamic other)`: Equality operator that performs deep
comparison when compared to another `JsonValue` of the same variant.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/topaz" gerrit_path="examples/fidl/fidl_packages/test/types_test.dart" region_tag="unions" adjust_indentation="auto" %}
```
#### Flexible unions and unknown variants
[Flexible unions][lang-unions] (that is, unions that are prefixed with the
`flexible` keyword in their FIDL definition) have an extra variant in the
generated tag class:
```dart
enum JsonValueTag {
$unknown,
intValue,
stringValue,
}
```
When a FIDL message containing a union with an unknown variant is decoded into
`JsonValue`, `JsonValue.$tag` returns `JsonValueTag.$unknown`, and
`JsonValue.$ordinal` returns the unknown ordinal.
Encoding a union with an unknown variant writes the unknown data and the
original ordinal back onto the wire.
Non-flexible (i.e. `strict`) unions fail when decoding a data containing an
unknown variant.
### Tables {#tables}
Given the [table][lang-tables] definition:
```fidl
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="tables" %}
```
The FIDL toolchain generates a `User` class that defines the following methods:
* `const User({age, name})`: Constructor for `User`.
* `int get age`: Getter for the `age` field.
* `String get name`: Getter for the `name` field.
* `Map<int, dynamic> get $fields`: Returns a map of ordinals to field values.
* `bool operator ==(dynamic other)`: Equality operator that performs deep
comparison when compared to another `User`.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/topaz" gerrit_path="examples/fidl/fidl_packages/test/types_test.dart" region_tag="tables" adjust_indentation="auto" %}
```
## Protocols {#protocols}
Given the [protocol][lang-protocols]:
```fidl
protocol TicTacToe {
StartGame(bool start_first);
MakeMove(uint8 row, uint8 col) -> (bool success, GameState? new_state);
-> OnOpponentMove(GameState new_state);
};
```
Note: The `MakeMove` method above returns a bool representing success, and a
nullable response value. This is considered un-idiomatic, you should use an [error type](#protocols-results)
instead.
FIDL generates an abstract `TicTacToe` class, which defines the interface of the
service used by clients to proxy calls to the server, and for the server for
implementing the protocol.
`TicTacToe` contains a `static const String $serviceName`, which is defined
depending on the presence of the [[Transitional] attribute](#transitional).
`TicTacToe` has the following abstract methods, representing the protocol
methods:
* `async.Future<void> startGame(bool start_first)`: Abstract method for a fire
and forget protocol method. It takes as arguments the request parameters and
returns a future of `void`.
* `async.Future<TicTacToe$MakeMove$Response> makeMove(int row, int col)`:
Abstract method for a two way protocol method. It takes as arguments the
request parameters and returns a [future of the response
type](#response-event-parameters).
* `async.Stream<GameState> get onOpponentMove`: Getter for a `Stream` of
`onOpponentMove` events.
### Client {#proxy}
The FIDL toolchain generates a `TicTacToeProxy` class that extends
`fidl.AsyncProxy<TicTacToe>`, and provides an implementation for the abstract
`TicTacToe` class that encodes and sends the request to the server end of the
channel.
Example client code could thus look like the following:
```dart
final tictactoe = fidl_tictactoe.TicTacToeProxy();
// ...bind the proxy, omitted from this example
tictactoe.startGame(true);
final state = await tictactoe.makeMove(0, 0);
```
Examples on how to set up and bind a proxy class to a channel are covered in the
[Dart tutorial][dart-tutorial].
### Server {#server}
Implementing a server for a FIDL protocol involves providing a concrete
implementation of `TicTacToe` abstract class.
The bindings provide a `TicTacToeBinding` class that can bind to a `TicTacToe` instance and a
channel, and listens to incoming messages on the channel, dispatches them to the server
implementation, and sends messages back through the channel. This class implements
<!-- TODO(fxbug.dev/58672) add link to API docs when those are available -->
`fidl.AsyncBinding<TicTacToe>`.
Examples on how to set up and bind a server implementation are covered in the
[Dart tutorial][dart-tutorial].
### Events {#events}
#### Client
The `TicTacToeProxy` class automatically implements the `onOpponentMove` getter.
Clients obtain an `async.Stream` of `onOpponentMove` events sent from the server
using this getter.
#### Server
<!-- TODO(fxbug.dev/58672) add link to API docs when those are available -->
Servers send events by implementing the `onOpponentMove` getter on the abstract
`TicTacToe` class. A `TicTacToeBinding` (see [tutorial][dart-tutorial]) that is
bound to an instance of `TicTacToe` that has implemented the `onOpponentMove`
getter will listen for events on the returned `async.Stream`, forwarding them to
the client.
### Results {#protocols-results}
Given the method with an error type:
```fidl
protocol TicTacToe {
MakeMove(uint8 row, uint8 col) -> (GameState new_state) error MoveError;
};
```
The method signature for `MakeMove` on the generated abstract `TicTacToe` class
is:
```dart
async.Future<GameState> makeMove(int row, int col)
```
The encapsulated `Future` corresponds to the generated [response
type](#response-event-parameters) for the success case, and the error case is
represented by having the server implementation or the proxy class throw a
`fidl.MethodException`.
Note: Unlike the [previous example](#protocols), the response type is just a
`Future<GameState>` instead of a `TicTacToe$MakeMove$Response` class. This is
because the method went from having two parameters to one parameter,
following the [response and event type rules](#response-event-parameters).
Using this feature, an example implementation of `MakeMove` on the server side
could look like:
```dart
@override
async.Future<GameState> makeMove(int row, int col) {
if (row > 9 || col > 9) {
return async.Future.error(fidl.MethodException(MoveError.OutOfBounds));
}
return async.Future.value(doSomething(row, col));
}
```
The `TicTacToeBinding` class will `catch` `fidl.MethodException`s and encode it
as an error.
An example of using this on the client side would be:
```dart
myproxy.makeMove(1, 2).then((gameState) { ... })
.catchError((moveError) { ... });
```
### Protocol composition {#protocol-composition}
FIDL does not have a concept of inheritance, and generates full code as
described above for all [composed protocols][lang-protocol-composition]. In
other words, the code generated for:
```fidl
protocol A {
Foo();
};
protocol B {
compose A;
Bar();
};
```
Provides the same API as the code generated for:
```fidl
protocol A {
Foo();
};
protocol B {
Foo();
Bar();
};
```
The generated code is identical except for the method ordinals.
### Protocol and method attributes {#protocol-and-method-attributes}
#### Transitional {#transitional}
For protocol methods annotated with the
[`[Transitional]`](/docs/reference/fidl/language/attributes.md#transitional)
attribute, the FIDL toolchain generates a default implementation on the abstract
class so that server implementations will continue to compile without having to
override the new method.
#### Discoverable {#discoverable}
The generated class for a protocol annotated with the
[`[Discoverable]`](/docs/reference/fidl/language/attributes.md#discoverable)
attribute has a non-null `$serviceName` field.
### Test scaffolding {#test-scaffolding}
The FIDL toolchain generates a `fidl_test.dart` file that contains convenience
code for testing FIDL server implementations. This file contains a class for
each protocol that provides stub implementations for each of the classs
methods, making it possible to implement only the methods that are used during
testing.
Given the example protocol above, The FIDL toolchain generates a
`TicTacToe$TestBase` class that extends the `TicTacToe` abstract class. All
methods are implemented by returning `async.Future.error(UnimplementedError())`,
and all events are implemented by returning a Stream with a single
`UnimplementedError` event.
<!-- xrefs -->
[dart-tutorial]: /docs/development/languages/fidl/tutorials/dart
[lang-constants]: /docs/reference/fidl/language/language.md#constants
[lang-bits]: /docs/reference/fidl/language/language.md#bits
[lang-enums]: /docs/reference/fidl/language/language.md#enums
[lang-structs]: /docs/reference/fidl/language/language.md#structs
[lang-tables]: /docs/reference/fidl/language/language.md#tables
[lang-unions]: /docs/reference/fidl/language/language.md#unions
[lang-protocols]: /docs/reference/fidl/language/language.md#protocols
[lang-protocol-composition]: /docs/reference/fidl/language/language.md#protocol-composition
[union-lexicon]: /docs/reference/fidl/language/lexicon.md#union-terms