blob: 297a0a241f9a84d3477c77d6690e7a15cd6d45b6 [file] [log] [blame] [view]
# Dart bindings
## Libraries {#libraries}
Given the library declaration:
```fidl
library games.tictactoe;
```
The bindings code for this library is generated into a
`fidl_games_tictactoe_async` dart library. The `fidl_` prefix and `_async`
suffix are hardcoded by the FIDL toolchain.
This code can then be imported using:
```dart
import 'package:fidl_games_tictactoe/fidl_async.dart' as tictactoe;
```
## Constants {#constants}
All [constants][lang-constants] are generated as a `const`. For example, the
following constants:
```fidl
const uint8 BOARD_SIZE = 9;
const string NAME = "Tic-Tac-Toe";
```
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
bits FileMode : uint16 {
READ = 0b001;
WRITE = 0b010;
EXECUTE = 0b100;
};
```
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.
### Enums {#enums}
Given the [enum][lang-enums] definition:
```fidl
enum Color {
RED = 1;
GREEN = 2;
BLUE = 3;
};
```
The FIDL toolchain generates a `Color` class with `static const` variables for
each enum member:
* `static const Color red`
* `static const Color green`
* `static const Color blue`
As well as the following variables:
* `static const Map<String, Color> $valuesMap`: A mapping ofthe string
representation of the member (`'red'`, `'green'`, or `'blue'`) to its
corresponding enum value (`Color.red`, `Color.green`, or `Color.blue`)
* `static const List<Color> $values`: A list of all of the Colors.
`Color` provides the following methods:
* `factory Color(int v)`: Factory constructor that returns the corresponding
`Color` static const variable (`red`, `green`, or `blue`) if the input matches
one of the discriminants, or `null` otherwise.
* `static Color $valueOf(String name)`: Look up a string name in the
`$valuesMap`.
* `String toString()`: Returns a readable representation of the `Color`.
### Structs {#structs}
Given the [struct][lang-structs] declaration:
```fidl
struct Color {
uint32 id;
string name = "red";
};
```
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`.
* `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`.
### Unions {#unions}
Given the union definition:
```fidl
union JsonValue {
1: reserved;
2: int32 int_value;
3: string string_value;
};
```
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`: Setter for the underlying [ordinal][union-lexicon] value.
* `Object get $data`: Setter for the underlying union data.
* `bool operator ==(dynamic other)`: Equality operator that performs deep
comparison when compared to another `JsonValue` of the same variant.
#### 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:
```table
table User {
1: reserved;
2: uint8 age;
3: string name;
};
```
The FIDL toolchain generates a `User` class that defines the following methods:
* `const User({age, name})`: Constructor for `User`.
* `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`.
## 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
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
Implementing a server for a FIDL protocol involves providing a concrete
implementation of `TicTacToe` abstract class.
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 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
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/tutorial-dart.md
[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