blob: 49466f62751bec2ae1a4a142b62bff29ef60bb03 [file] [log] [blame] [view] [edit]
# 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/fuchsia" gerrit_path="examples/fidl/dart/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 flag set (`$none`)
or every flag set (`$mask`):
* `static const FileMode read`
* `static const FileMode write`
* `static const FileMode execute`
* `static const FileMode $none`
* `static const FileMode $mask`
`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.
* `int getUnknownBits()`: Returns only the set bits that are unknown. Always
returns 0 for [strict][lang-flexible] bits.
* `bool hasUnknownBits()`: Returns whether this value contains any unknown bits.
Always returns `false` for [strict][lang-flexible] bits.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/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 of the 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 enum values.
If `LocationType` is [flexible][lang-flexible], it will have an unknown
placeholder member as well:
* `static const LocationType $unknown`
If the enum has a member tagged with the [`[Unknown]`][unknown-attr] attribute,
the placeholder variable will have the same value as the tagged unknown
member.
`LocationType` provides the following methods:
* `static LocationType $valueOf(String name)`: Look up a string name in the
`$valuesMap`.
* `String toString()`: Returns a readable representation of the `LocationType`.
* `bool isUnknown()`: Returns whether this enum is unknown. Always returns
`false` for [strict][lang-flexible] enums.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/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/fuchsia" gerrit_path="examples/fidl/dart/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.
* `fidl.UnknownRawData? get $unknownData`: Returns the bytes and handles of the
unknown data if this union contains an unknown variant, or `null` otherwise.
Always returns `null` for [strict][lang-flexible] unions.
If `JsonValue` is [flexible][lang-flexible], it will have the following
additional methods:
* `const JsonValue.with$UnknownData(int ordinal, fidl.UnknownRawData data)`:
Constructor for a value with an unknown variant set. This should only be used
for testing, e.g. to check that code handles unknown unions correctly.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/fidl_packages/test/types_test.dart" region_tag="unions" adjust_indentation="auto" %}
```
#### Flexible unions and unknown variants
[Flexible][lang-flexible] unions 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.
[Strict][lang-flexible] unions fail when decoding an unknown variant.
[Flexible][lang-flexible] unions that are [value][lang-resource] types fail when
decoding an unknown variant with handles.
### 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({$unknownData, age, name})`: Constructor for `User`. Contains an
optional parameter for each field as well as a map containing any unknown
fields, as a `Map<int, fidl.UnknownRawData>`. Specifying a value for the
unknown fields should only be done for testing, e.g. to test that a table with
unknown fields is handled correctly.
* `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.
* `Map<int, fidl.UnknownRawData>? get $unknownData`: Returns a map of ordinals
to unknown field values (i.e. bytes and handles). The list of handles is
returned in [traversal order][traversal], and is guaranteed to be empty if the
table is a [value][lang-resource] type.
* `bool operator ==(dynamic other)`: Equality operator that performs deep
comparison when compared to another `User`.
Example usage:
```dart
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/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-flexible]: /docs/reference/fidl/language/language.md#strict-vs-flexible
[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-resource]: /docs/reference/fidl/language/language.md#value-vs-resource
[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
[unknown-attr]: /docs/reference/fidl/language/attributes.md#unknown
[traversal]: /docs/reference/fidl/language/wire-format/README.md#traversal-order