| // 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. |
| |
| package codegen |
| |
| const protocolTmpl = ` |
| {{- define "Params" -}} |
| {{- range $index, $param := . -}} |
| {{- if $index }}, {{ end -}}{{ $param.Type.Decl }} {{ $param.Name }} |
| {{- end -}} |
| {{ end }} |
| |
| {{/* 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 }}.decodeResponseInlineSize(), $types[0]) |
| {{- else }} |
| {{- if .AsyncResponseClass -}} |
| $fidl.decodeMessageWithCallback<{{ .AsyncResponseClass }}>( |
| $message, |
| {{ .TypeSymbol }}.decodeResponseInlineSize(), |
| ($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 }}.decodeResponseInlineSize(), $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 .Response.HasError)) -}} |
| $fidl.encodeMessageWithCallback( |
| $encoder, |
| {{ .TypeSymbol }}.encodingResponseInlineSize(), |
| () { |
| {{- range $index, $response := .Response.WireParameters }} |
| $types[{{ $index }}].encode($encoder, $response.{{ .Name }}, $fidl.kMessageHeaderSize, 1); |
| {{- end -}} |
| } |
| ); |
| {{- else -}} |
| {{- if .Response.WireParameters -}} |
| $fidl.encodeMessage($encoder, {{ .TypeSymbol }}.encodingResponseInlineSize(), $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 .ServiceName }} |
| extends $fidl.Service |
| {{- end }} |
| { |
| {{- if .ServiceName }} |
| static const String $serviceName = {{ .ServiceName }}; |
| @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 }} |
| } |
| |
| // TODO: Remove ServiceData for non-service |
| class {{ .ServiceData }} implements $fidl.ServiceData<{{ .Name }}> { |
| |
| const {{ .ServiceData }}(); |
| |
| @override |
| String getName() { |
| {{- if .ServiceName }} |
| 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 .ServiceName }} |
| $serviceName: {{ .ServiceName }}, |
| {{- end }} |
| $interfaceName: r'{{ .Name }}') |
| ) { |
| ctrl.onResponse = _handleResponse; |
| |
| {{- if .HasEvents }} |
| ctrl.whenClosed.then((_) { |
| {{- range .Methods }} |
| {{- if not .HasRequest }} |
| {{- if .HasResponse }} |
| _{{ .Name }}EventStreamController.close(); |
| {{- end }} |
| {{- end }} |
| {{- end }} |
| }, onError: (_) { }); |
| {{- end }} |
| } |
| |
| {{- if .ServiceName }} |
| @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; |
| try { |
| Timeline.startSync(_name); |
| final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!; |
| _{{ .Name }}EventStreamController.add( |
| {{- template "DecodeResponse" . -}} |
| ); |
| } catch(_e) { |
| ctrl.proxyError($fidl.FidlError('Exception handling event $_name: $_e')); |
| ctrl.close(); |
| rethrow; |
| } finally { |
| Timeline.finishSync(); |
| } |
| break; |
| {{- end }} |
| {{- end }} |
| {{- end }} |
| default: |
| ctrl.proxyError($fidl.FidlError('Unexpected message ordinal: ${$message.ordinal}')); |
| ctrl.close(); |
| 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; |
| try { |
| Timeline.startSync(_name); |
| final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!; |
| // ignore: prefer_const_declarations |
| final $response = {{- template "DecodeResponse" . -}}; |
| {{ if .Response.HasError }} |
| if ($response.$tag == {{ .Response.ResultType.TagName }}.response) { |
| {{ if .AsyncResponseClass }} |
| $completer.complete( |
| {{ .AsyncResponseClass }}( |
| {{ range $param := .Response.MethodParameters }} |
| $response.response.{{ $param.Name }}, |
| {{ end }} |
| )); |
| {{ else }} |
| {{ if (eq .AsyncResponseType "void") }} |
| $completer.complete(null); |
| {{ else }} |
| $completer.complete($response.response.{{ (index .Response.MethodParameters 0).Name }}); |
| {{ end }} |
| {{ end }} |
| } else { |
| $completer.completeError($fidl.MethodException($response.err)); |
| } |
| {{ else }} |
| $completer.complete($response); |
| {{ end }} |
| } catch(_e) { |
| ctrl.proxyError($fidl.FidlError('Exception handling method response $_name: $_e')); |
| ctrl.close(); |
| rethrow; |
| } finally { |
| Timeline.finishSync(); |
| } |
| break; |
| {{- end }} |
| {{- end }} |
| {{- end }} |
| default: |
| ctrl.proxyError($fidl.FidlError('Unexpected message ordinal: ${$message.ordinal}')); |
| ctrl.close(); |
| break; |
| } |
| } |
| |
| {{- range .Methods }} |
| {{- if .HasRequest }} |
| {{- range .Doc }} |
| ///{{ . -}} |
| {{- end }} |
| @override |
| {{ template "AsyncReturn" . }} {{ .Name }}({{ template "AsyncParams" .Request }}) async { |
| if (!ctrl.isBound) { |
| return $async.Future.error($fidl.FidlStateException('Proxy<${ctrl.$interfaceName}> is closed.'), StackTrace.current); |
| } |
| |
| final $fidl.Encoder $encoder = $fidl.Encoder(); |
| $encoder.encodeMessageHeader({{ .OrdinalName }}, 0); |
| {{- if .Request }} |
| final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.request!; |
| $fidl.encodeMessageWithCallback( |
| $encoder, |
| {{ .TypeSymbol }}.encodingRequestInlineSize(), |
| () { |
| {{- 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 }} |
| } |
| |
| class {{ .BindingName }} extends $fidl.AsyncBinding<{{ .Name }}> { |
| {{ .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.listen(($response) { |
| final $fidl.Encoder $encoder = $fidl.Encoder(); |
| $encoder.encodeMessageHeader({{ .OrdinalName }}, 0); |
| final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!; |
| {{ template "EncodeResponse" . }} |
| sendMessage($encoder.message); |
| })); |
| } |
| {{- 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; |
| try { |
| Timeline.startSync(_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 }}.decodeRequestInlineSize(), |
| ($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) { |
| {{ if .AsyncResponseClass }} |
| return {{ .Response.ResultType.Name }}.withResponse( |
| {{ .Response.ValueType.Decl }}( |
| {{ range $param := .Response.MethodParameters }} |
| {{ $param.Name }}: $responseValue.{{ $param.Name }}, |
| {{ end }} |
| )); |
| {{ else }} |
| return {{ .Response.ResultType.Name }}.withResponse( |
| {{ .Response.ValueType.Decl }}( |
| {{ if (ne .AsyncResponseType "void") }} |
| {{ (index .Response.MethodParameters 0).Name }}: $responseValue |
| {{ end }} |
| )); |
| {{ end }} |
| }, onError: ($error) { |
| if ($error is $fidl.MethodException) { |
| return {{ .Response.ResultType.Name }}.withErr($error.value); |
| } else { |
| return Future.error($error, StackTrace.current); |
| } |
| }) |
| {{ end }} |
| .then(($response) { |
| final $fidl.Encoder $encoder = $fidl.Encoder(); |
| $encoder.encodeMessageHeader({{ .OrdinalName }}, $message.txid); |
| {{- if .Response.WireParameters }} |
| final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response!; |
| {{ template "EncodeResponse" . -}} |
| {{- end }} |
| $respond($encoder.message); |
| }, onError: (_e) { |
| close(); |
| print('Exception handling method call $_name: $_e'); |
| }); |
| {{- end }} |
| } catch(_e) { |
| close(); |
| print('Exception handling method call $_name: $_e'); |
| rethrow; |
| } finally { |
| Timeline.finishSync(); |
| } |
| break; |
| {{- end }} |
| {{- end }} |
| default: |
| throw $fidl.FidlError(r'Unexpected message name for {{ .BindingName }}'); |
| } |
| } |
| } |
| |
| {{ end }} |
| |
| |
| |
| {{- define "ProtocolTestDeclaration" -}} |
| |
| class {{ .Name }}$TestBase extends {{ .Name }} { |
| {{- $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 }} |
| |
| } |
| |
| {{ end }} |
| |
| ` |