blob: bff5c2a1bb248bc6c5e7d6510f57e138bfb8ac5b [file] [log] [blame]
{{/*
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
*/}}
{{/* Generate a parameter list (eg "int foo, String baz") with AsyncDecl types */}}
{{- define "AsyncParams" -}}
{{- range $index, $param := . -}}
{{- if $index }}, {{ end -}}{{ $param.Type.Decl }} {{ $param.Name }}
{{- end -}}
{{ end }}
{{- define "AsyncReturn" -}}
{{- if .HasResponse -}}
$async.Future<{{ .AsyncResponseType }}>
{{- else -}}
$async.Future<void>
{{- end -}}
{{- end -}}
{{- define "ForwardParams" -}}
{{ range $index, $param := . }}{{ if $index }}, {{ end }}{{ $param.Name }}{{ end }}
{{- end -}}
{{/*
Decode a method response message.
The current object is the method (ir.Method).
The Dart local variables are:
List<$fidl.MemberType> $types - the table for the response.
$fidl.Message $message - the message being decoded.
This template expands to an expression so it can be assigned or passed as an argument.
*/}}
{{- define "DecodeResponse" -}}
{{- if .Response.HasError }}
$fidl.decodeMessage($message, {{ .TypeSymbol }}.responseInlineSize($message.wireFormat), $types[0])
{{- else }}
{{- if .AsyncResponseClass -}}
$fidl.decodeMessageWithCallback<{{ .AsyncResponseClass }}>(
$message,
{{ .TypeSymbol }}.responseInlineSize($message.wireFormat),
($fidl.Decoder decoder) {
return {{ .AsyncResponseClass }}(
{{- range $index, $response := .Response.WireParameters }}
$types[{{ $index }}].decode(decoder, $fidl.kMessageHeaderSize, 1),
{{- end -}}
);
}
)
{{- else -}}
{{- if .Response.WireParameters -}}
$fidl.decodeMessage(
$message, {{ .TypeSymbol }}.responseInlineSize($message.wireFormat), $types[0])
{{- else -}}
null
{{- end -}}
{{- end -}}
{{- end -}}
{{ end -}}
{{/*
Encode a method response message.
The current object is the method (ir.Method).
The Dart local variables are:
List<$fidl.MemberType> $types - the table for the response.
$fidl.Encoder $encoder - the encoder for the message.
$response - the Dart response type.
This template expands to a statement.
*/}}
{{- define "EncodeResponse" -}}
{{- if (and .AsyncResponseClass (not (or .Response.HasError .Response.HasTransportError))) -}}
$fidl.encodeMessageWithCallback(
$encoder,
{{ .TypeSymbol }}.responseInlineSize($encoder.wireFormat),
() {
{{- range $index, $response := .Response.WireParameters }}
$types[{{ $index }}].encode($encoder, $response.{{ .Name }}, $fidl.kMessageHeaderSize, 1);
{{- end -}}
}
);
{{- else -}}
{{- if .Response.WireParameters -}}
$fidl.encodeMessage(
$encoder,
{{ .TypeSymbol }}.responseInlineSize($encoder.wireFormat),
$types[0],
$response);
{{- end -}}
{{- end -}}
{{ end -}}
{{- define "ProtocolAsyncDeclaration" -}}
{{ range .Methods }}
// {{ .Name }}: {{ if .HasRequest }}({{ template "AsyncParams" .Request }}){{ end -}}
{{- if .HasResponse }} -> ({{ template "AsyncParams" .Response.MethodParameters }}){{ end }}
const int {{ .OrdinalName }} = {{ .Ordinal | printf "%#x" }};
const $fidl.MethodType {{ .TypeSymbol }} = {{ .TypeExpr }};
{{- end }}
{{- range .Methods }}
{{- if .AsyncResponseClass }}
class {{ .AsyncResponseClass }} {
{{- range .Response.MethodParameters }}
final {{ .Type.Decl }} {{ .Name }};
{{- end }}
{{ .AsyncResponseClass }}(
{{- range .Response.MethodParameters }}
this.{{ .Name }},
{{- end -}}
);
}
{{- end }}
{{- end }}
{{- range .Doc }}
///{{ . -}}
{{- end }}
abstract class {{ .Name }}
{{- if .GetServiceName }}
extends $fidl.Service
{{- end }}
{
{{- if .GetServiceName }}
static const String $serviceName = {{ .GetServiceName }};
@override
{{- end }}
$fidl.ServiceData? get $serviceData => {{ .ServiceData }}();
{{- range .Methods }}
{{- if .HasRequest }}
{{- range .Doc }}
///{{ . -}}
{{- end }}
{{ template "AsyncReturn" . }} {{ .Name }}({{ template "AsyncParams" .Request }})
{{- if .Transitional }}
{ return $async.Future.error(UnimplementedError(), StackTrace.current); }
{{- else }}
;
{{- end }}
{{- else }}
{{- range .Doc }}
///{{ . -}}
{{- end }}
$async.Stream<{{ .AsyncResponseType}}>? get {{ .Name }}
{{- if .Transitional }}
{ return $async.Stream.empty(); }
{{- else }}
;
{{- end }}
{{- end }}
{{- end }}
}
{{- if .OneWayUnknownInteractions }}
abstract class {{ .Name }}Server extends {{ .Name }} {
$async.Future<void> $unknownOneWay(int ordinal);
{{- if .TwoWayUnknownInteractions }}
$async.Future<void> $unknownTwoWay(int ordinal);
{{ end }}
}
{{ end -}}
// TODO: Remove ServiceData for non-service
class {{ .ServiceData }} implements $fidl.ServiceData<{{ .Name }}> {
const {{ .ServiceData }}();
@override
String getName() {
{{- if .GetServiceName }}
return {{ .Name }}.$serviceName;
{{- else }}
return "";
{{- end }}
}
@override
$fidl.AsyncBinding getBinding() {
return {{ .BindingName }}();
}
}
{{- range .Doc }}
///{{ . -}}
{{- end }}
class {{ .ProxyName }} extends $fidl.AsyncProxy<{{ .Name }}>
implements {{ .Name }} {
{{ .ProxyName }}() : super($fidl.AsyncProxyController<{{ .Name }}>(
{{- if .GetServiceName }}
$serviceName: {{ .GetServiceName }},
{{- end }}
$interfaceName: r'{{ .Name }}')
) {
ctrl.onResponse = _handleResponse;
{{- if or .HasEvents .OneWayUnknownInteractions }}
ctrl.whenClosed.then((_) {
{{- range .Methods }}
{{- if not .HasRequest }}
{{- if .HasResponse }}
_{{ .Name }}EventStreamController.close();
{{- end }}
{{- end }}
{{- end }}
{{- if .OneWayUnknownInteractions }}
_$unknownEventStreamController.close();
{{- end }}
}, onError: (_) { });
{{- end }}
}
{{- if .GetServiceName }}
@override
$fidl.ServiceData get $serviceData => {{ .ServiceData }}();
{{- else }}
@override
Null get $serviceData => null;
{{- end }}
void _handleEvent($fidl.IncomingMessage $message) {
switch ($message.ordinal) {
{{- range .Methods }}
{{- if not .HasRequest }}
{{- if .HasResponse }}
case {{ .OrdinalName }}:
final String _name = {{ .TypeSymbol }}.name;
$fidl.performCtrlWithExceptionHandling(_name, ctrl, 'event',
_{{ .Name }}EventStreamController.addError,
() {
final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!;
// ignore: prefer_const_declarations
final {{ .ResponseMessageType }} $response = {{- template "DecodeResponse" . -}};
{{ .AddEventResponse }}
});
break;
{{- end }}
{{- end }}
{{- end }}
default:
{{ if .OneWayUnknownInteractions -}}
switch ($message.strictness) {
case $fidl.CallStrictness.flexible:
const String _name = "{{ .Name }} [UnknownEvent]";
$fidl.performCtrlWithExceptionHandling(_name, ctrl, 'event',
_$unknownEventStreamController.addError,
() {
_$unknownEventStreamController.add($fidl.UnknownEvent($message.ordinal));
});
break;
case $fidl.CallStrictness.strict:
ctrl.proxyError($fidl.FidlError('Unexpected message ordinal: ${$message.ordinal}'));
break;
}
{{- else -}}
ctrl.proxyError($fidl.FidlError('Unexpected message ordinal: ${$message.ordinal}'));
{{- end }}
break;
}
}
void _handleResponse($fidl.IncomingMessage $message) {
final int $txid = $message.txid;
if ($txid == 0) {
_handleEvent($message);
return;
}
final $async.Completer? $completer = ctrl.getCompleter($txid);
if ($completer == null) {
$message.closeHandles();
return;
}
switch ($message.ordinal) {
{{- range .Methods }}
{{- if .HasRequest }}
{{- if .HasResponse }}
case {{ .OrdinalName }}:
final String _name = {{ .TypeSymbol }}.name;
$fidl.performCtrlWithExceptionHandling(_name, ctrl, 'method response',
$completer.completeError,
() {
final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!;
// ignore: prefer_const_declarations
final {{ .ResponseMessageType }} $response = {{- template "DecodeResponse" . -}};
{{ .AsyncResponseCompleter }}
});
break;
{{- end }}
{{- end }}
{{- end }}
default:
ctrl.proxyError($fidl.FidlError('Unexpected message ordinal: ${$message.ordinal}'));
break;
}
}
{{- range .Methods }}
{{- if .HasRequest }}
{{- range .Doc }}
///{{ . -}}
{{- end }}
@override
{{ template "AsyncReturn" . }} {{ .Name }}({{ template "AsyncParams" .Request }}) {
if (!ctrl.isBound) {
return $async.Future.error($fidl.FidlStateException('Proxy<${ctrl.$interfaceName}> is closed.'), StackTrace.current);
}
final $fidl.Encoder $encoder = $fidl.Encoder($fidl.kWireFormatDefault);
$encoder.encodeMessageHeader({{ .OrdinalName }}, 0, {{ .MessageHeaderStrictness }});
{{- if .Request }}
final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.request!;
$fidl.encodeMessageWithCallback(
$encoder,
{{ .TypeSymbol }}.requestInlineSize($encoder.wireFormat),
() {
{{- range $index, $request := .Request }}
$types[{{ $index }}].encode($encoder, {{ .Name }}, $fidl.kMessageHeaderSize, 1);
{{- end -}}
}
);
{{- end }}
{{- if .HasResponse }}
final $completer = $async.Completer<{{ .AsyncResponseType }}>();
ctrl.sendMessageWithResponse($encoder.message, $completer);
return $completer.future;
{{- else }}
return $async.Future.sync(() {
ctrl.sendMessage($encoder.message);
});
{{- end }}
}
{{ else }}
final _{{ .Name }}EventStreamController = $async.StreamController<{{ .AsyncResponseType }}>.broadcast();
{{- range .Doc }}
///{{ . -}}
{{- end }}
@override
$async.Stream<{{ .AsyncResponseType }}> get {{ .Name }} => _{{ .Name }}EventStreamController.stream;
{{ end }}
{{- end }}
{{- if .OneWayUnknownInteractions }}
final _$unknownEventStreamController = $async.StreamController<$fidl.UnknownEvent>.broadcast();
@override
$async.Stream<$fidl.UnknownEvent> get $unknownEvents => _$unknownEventStreamController.stream;
{{ end -}}
}
class {{ .BindingName }} extends $fidl.AsyncBinding<{{ .ServerName }}> {
{{ .BindingName }}() : super(r"{{ .Name }}")
{{- if .HasEvents }} {
final List<$async.StreamSubscription<dynamic>> $subscriptions = [];
void $unsubscribe() {
for (final $sub in $subscriptions) {
$sub.cancel();
}
$subscriptions.clear();
}
whenBound.then((_) {
final impl = this.impl;
if (impl != null) {
{{- range .Methods }}
{{- if not .HasRequest }}
final _{{ .Name }}_stream = impl.{{ .Name }};
if (_{{ .Name }}_stream != null) {
$subscriptions.add(_{{ .Name }}_stream
{{- if .Response.HasError }}
.transform($async.StreamTransformer.fromHandlers(
handleData: ({{ .AsyncResponseType }} $responseValue, $async.EventSink<{{ .Response.ResultTypeName }}> $sink) {
$sink.add({{ .AsyncResultResponse }});
},
handleError: (Object $error, StackTrace $stackTrace, $async.EventSink<{{ .Response.ResultTypeName }}> $sink) {
if ($error is $fidl.MethodException) {
$sink.add({{ .Response.ResultTypeName }}.withErr($error.value));
} else {
$sink.addError($error, $stackTrace);
}
}))
{{- end }}
.listen(($response) {
final $fidl.Encoder $encoder = $fidl.Encoder($fidl.kWireFormatDefault);
$encoder.encodeMessageHeader({{ .OrdinalName }}, 0, {{ .MessageHeaderStrictness }});
final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!;
{{ template "EncodeResponse" . }}
sendMessage($encoder.message);
},
// TODO: was ignoring errors intentional here? For methods, the
// channel gets closed on error.
onError: (_e) {
$fidl.handleException({{ .TypeSymbol }}.name, _e, close);
}));
}
{{- end }}
{{- end }}
}
});
whenClosed.then((_) => $unsubscribe());
}
{{- else -}}
;
{{- end }}
@override
void handleMessage($fidl.IncomingMessage $message, $fidl.OutgoingMessageSink $respond) {
switch ($message.ordinal) {
{{- range .Methods }}
{{- if .HasRequest }}
case {{ .OrdinalName }}:
final String _name = {{ .TypeSymbol }}.name;
$fidl.performWithExceptionHandling(_name, () {
final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.request!;
// ignore: prefer_const_declarations
final _impl = impl!;
final {{ template "AsyncReturn" . }} $future = $fidl.decodeMessageWithCallback<{{ template "AsyncReturn" . }}>(
$message,
{{ .TypeSymbol }}.requestInlineSize($message.wireFormat),
($fidl.Decoder decoder) {
return _impl.{{ .Name }}(
{{- range $index, $request := .Request }}
$types[{{ $index }}].decode(decoder, $fidl.kMessageHeaderSize, 1),
{{- end }}
);
}
);
{{- if .HasResponse }}
$future
{{ if .Response.HasError }}
.then(($responseValue) {
return {{ .AsyncResultResponse }};
}, onError: ($error) {
if ($error is $fidl.MethodException) {
return {{ .Response.ResultTypeName }}.withErr($error.value);
} else {
return Future.error($error, StackTrace.current);
}
})
{{ else if .HasTransportError }}
.then(($responseValue) {
return {{ .AsyncResultResponse }};
})
{{ end }}
.then(($response) {
final $fidl.Encoder $encoder = $fidl.Encoder($fidl.kWireFormatDefault);
$encoder.encodeMessageHeader({{ .OrdinalName }}, $message.txid, {{ .MessageHeaderStrictness }});
{{- if .Response.WireParameters }}
final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!;
{{ template "EncodeResponse" . -}}
{{- end }}
$respond($encoder.message);
}, onError: (_e) {
$fidl.handleException(_name, _e, close);
});
{{- end }}
}, close);
break;
{{- end }}
{{- end }}
default:
{{ if .TwoWayUnknownInteractions -}}
switch ($message.strictness) {
case $fidl.CallStrictness.flexible:
if ($message.txid == 0) {
impl!.$unknownOneWay($message.ordinal).catchError((_e) {
$fidl.handleException("unknown one-way", _e, close);
});
} else {
final $fidl.Encoder $encoder = $fidl.Encoder($fidl.kWireFormatDefault);
$encoder.encodeUnknownMethodResponse($message.ordinal, $message.txid);
$respond($encoder.message);
impl!.$unknownTwoWay($message.ordinal).catchError((_e) {
$fidl.handleException("unknown one-way", _e, close);
});
}
break;
case $fidl.CallStrictness.strict:
throw $fidl.FidlError(r'Unexpected message name for {{ .BindingName }}');
}
break;
{{- else if .OneWayUnknownInteractions -}}
switch ($message.strictness) {
case $fidl.CallStrictness.flexible:
if ($message.txid == 0) {
impl!.$unknownOneWay($message.ordinal).catchError((_e) {
$fidl.handleException("unknown one-way", _e, close);
});
break;
} else {
continue $flexibleTwoWay;
}
$flexibleTwoWay:
case $fidl.CallStrictness.strict:
throw $fidl.FidlError(r'Unexpected message name for {{ .BindingName }}');
}
break;
{{- else -}}
throw $fidl.FidlError(r'Unexpected message name for {{ .BindingName }}');
{{- end }}
}
}
}
{{ end }}
{{- define "ProtocolTestDeclaration" -}}
class {{ .Name }}$TestBase extends {{ .ServerName }} {
{{- $protocolName := .Name }}
{{- range .Methods }}
{{- $exceptionMessage := printf "r'%s not implemented on %s test base. Please implement.'" .Name $protocolName }}
@override
{{- if .HasRequest }}
{{ template "AsyncReturn" . }} {{ .Name }}({{ template "AsyncParams" .Request }}) {
return $async.Future.error(UnimplementedError({{ $exceptionMessage }}), StackTrace.current);
}
{{- else }}
$async.Stream<{{ .AsyncResponseType }}> get {{ .Name }} {
return $async.Stream.fromFuture($async.Future.error(UnimplementedError({{ $exceptionMessage }}), StackTrace.current));
}
{{- end }}
{{- end }}
{{- if .OneWayUnknownInteractions }}
$async.Future<void> $unknownOneWay(int ordinal) {
return $async.Future.error(UnimplementedError({{ printf "r'$unknownOneWay not implemented on %s test base. Please implement.'" .Name }}), StackTrace.current);
}
{{- if .TwoWayUnknownInteractions }}
$async.Future<void> $unknownTwoWay(int ordinal) {
return $async.Future.error(UnimplementedError({{ printf "r'$unknownTwoWay not implemented on %s test base. Please implement.'" .Name }}), StackTrace.current);
}
{{ end }}
{{ end -}}
}
{{ end }}