| // Copyright 2021 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 fidlgen_cpp |
| |
| import ( |
| "fmt" |
| "strings" |
| |
| "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen" |
| ) |
| |
| const kMessageHeaderSize = 16 |
| |
| // |
| // Generate code for sending and receiving FIDL messages i.e. the messaging API. |
| // |
| |
| // hlMessagingDetails represents the various generated definitions associated |
| // with a protocol, in the high-level C++ bindings. |
| // TODO(fxbug.dev/72798): Use the same approach to pass wireTypeNames and |
| // hlMessagingDetails to the templates. |
| type hlMessagingDetails struct { |
| // ProtocolMarker is a pure-virtual interface corresponding to methods in |
| // the protocol. Notably, HLCPP shares the same interface type between |
| // the server and client bindings API. |
| ProtocolMarker name |
| |
| // InterfaceAliasForStub is the type alias generated within the |
| // "Stub" class, that refers to the pure-virtual interface corresponding to |
| // the protocol. |
| InterfaceAliasForStub name |
| |
| // Proxy implements the interface by encoding and making method calls. |
| Proxy name |
| |
| // Stub calls into the interface after decoding an incoming message. |
| // It also implements the EventSender interface. |
| Stub name |
| |
| // EventSender is a pure-virtual interface for sending events. |
| EventSender name |
| |
| // SyncInterface is a pure-virtual interface for making synchronous calls. |
| SyncInterface name |
| |
| // SyncProxy implements the SyncInterface. |
| SyncProxy name |
| |
| RequestEncoder name |
| RequestDecoder name |
| ResponseEncoder name |
| ResponseDecoder name |
| } |
| |
| func compileHlMessagingDetails(protocol nameVariants) hlMessagingDetails { |
| p := protocol.HLCPP |
| stub := p.appendName("_Stub") |
| return hlMessagingDetails{ |
| ProtocolMarker: p, |
| InterfaceAliasForStub: stub.nest(p.appendName("_clazz").Name()), |
| Proxy: p.appendName("_Proxy"), |
| Stub: stub, |
| EventSender: p.appendName("_EventSender"), |
| SyncInterface: p.appendName("_Sync"), |
| SyncProxy: p.appendName("_SyncProxy"), |
| RequestEncoder: p.appendName("_RequestEncoder"), |
| RequestDecoder: p.appendName("_RequestDecoder"), |
| ResponseEncoder: p.appendName("_ResponseEncoder"), |
| ResponseDecoder: p.appendName("_ResponseDecoder"), |
| } |
| } |
| |
| type protocolWithHlMessaging struct { |
| Protocol |
| hlMessagingDetails |
| } |
| |
| // WithHlMessaging returns a new protocol IR where the HLCPP bindings details |
| // are promoted to the same naming scope as the protocol. This makes it easier |
| // to access the HLCPP details in golang templates. |
| func (p Protocol) WithHlMessaging() protocolWithHlMessaging { |
| return protocolWithHlMessaging{ |
| Protocol: p, |
| hlMessagingDetails: p.hlMessaging, |
| } |
| } |
| |
| // TODO(fxbug.dev/60240): Start implementing unified bindings messaging layer |
| // based on this skeleton. |
| type unifiedMessagingDetails struct { |
| ClientImpl name |
| EventHandlers name |
| Interface name |
| EventSender name |
| RequestEncoder name |
| RequestDecoder name |
| ResponseEncoder name |
| ResponseDecoder name |
| } |
| |
| // These correspond to templated classes forward-declared in |
| // //zircon/system/ulib/fidl/include/lib/fidl/llcpp/wire_messaging.h |
| var ( |
| // Protocol related |
| WireSyncClient = fidlNs.member("WireSyncClient") |
| WireClient = fidlNs.member("WireClient") |
| WireEventHandlerInterface = internalNs.member("WireEventHandlerInterface") |
| WireSyncEventHandler = fidlNs.member("WireSyncEventHandler") |
| WireAsyncEventHandler = fidlNs.member("WireAsyncEventHandler") |
| WireServer = fidlNs.member("WireServer") |
| WireEventSender = fidlNs.member("WireEventSender") |
| WireWeakEventSender = internalNs.member("WireWeakEventSender") |
| WireClientImpl = internalNs.member("WireClientImpl") |
| WireSyncClientImpl = internalNs.member("WireSyncClientImpl") |
| WireSyncBufferClientImpl = internalNs.member("WireSyncBufferClientImpl") |
| WireServerDispatcher = internalNs.member("WireServerDispatcher") |
| |
| // Method related |
| WireRequest = fidlNs.member("WireRequest") |
| WireResponse = fidlNs.member("WireResponse") |
| WireResult = fidlNs.member("WireResult") |
| WireUnownedResult = fidlNs.member("WireUnownedResult") |
| WireResponseContext = fidlNs.member("WireResponseContext") |
| WireCompleter = internalNs.member("WireCompleter") |
| WireCompleterBase = internalNs.member("WireCompleterBase") |
| WireMethodTypes = internalNs.member("WireMethodTypes") |
| WireRequestView = internalNs.member("WireRequestView") |
| ) |
| |
| type wireTypeNames struct { |
| // WireProtocolMarker is a class only used for containing other definitions |
| // related to this protocol. |
| // TODO(fxbug.dev/72798): Golang template should use this instead of the |
| // nameVariants embedded in Protocol. |
| WireProtocolMarker name |
| WireSyncClient name |
| WireClient name |
| WireEventHandlerInterface name |
| WireSyncEventHandler name |
| WireAsyncEventHandler name |
| WireServer name |
| WireEventSender name |
| WireWeakEventSender name |
| WireClientImpl name |
| WireSyncClientImpl name |
| WireSyncBufferClientImpl name |
| WireServerDispatcher name |
| } |
| |
| func newWireTypeNames(protocolVariants nameVariants) wireTypeNames { |
| p := protocolVariants.Wire |
| return wireTypeNames{ |
| WireProtocolMarker: p, |
| WireSyncClient: WireSyncClient.template(p), |
| WireClient: WireClient.template(p), |
| WireEventHandlerInterface: WireEventHandlerInterface.template(p), |
| WireSyncEventHandler: WireSyncEventHandler.template(p), |
| WireAsyncEventHandler: WireAsyncEventHandler.template(p), |
| WireServer: WireServer.template(p), |
| WireEventSender: WireEventSender.template(p), |
| WireWeakEventSender: WireWeakEventSender.template(p), |
| WireClientImpl: WireClientImpl.template(p), |
| WireSyncClientImpl: WireSyncClientImpl.template(p), |
| WireSyncBufferClientImpl: WireSyncBufferClientImpl.template(p), |
| WireServerDispatcher: WireServerDispatcher.template(p), |
| } |
| } |
| |
| // protocolInner contains information about a Protocol that should be |
| // filled out by the compiler. |
| type protocolInner struct { |
| Attributes |
| // TODO(fxbug.dev/72798): This should be replaced by ProtocolMarker in hlMessagingDetails |
| // and wireMessagingDetails. In particular, the unified bindings do not declare |
| // protocol marker classes. |
| nameVariants |
| |
| // [Discoverable] protocols are exported to the outgoing namespace under this |
| // name. This is deprecated by FTP-041 unified services. |
| // TODO(fxbug.dev/8035): Remove. |
| DiscoverableName string |
| |
| hlMessaging hlMessagingDetails |
| wireTypeNames |
| |
| // ClientAllocation is the allocation behavior of the client when receiving |
| // FIDL events over this protocol. |
| SyncEventAllocationV1 allocation |
| SyncEventAllocationV2 allocation |
| Methods []Method |
| FuzzingName string |
| TestBase nameVariants |
| } |
| |
| // Protocol should be created using newProtocol. |
| type Protocol struct { |
| protocolInner |
| |
| // OneWayMethods contains the list of one-way (i.e. fire-and-forget) methods |
| // in the protocol. |
| OneWayMethods []*Method |
| |
| // TwoWayMethods contains the list of two-way (i.e. has both request and |
| // response) methods in the protocol. |
| TwoWayMethods []*Method |
| |
| // ClientMethods contains the list of client-initiated methods (i.e. any |
| // interaction that is not an event). It is the union of one-way and two-way |
| // methods. |
| ClientMethods []*Method |
| |
| // Events contains the list of events (i.e. initiated by servers) |
| // in the protocol. |
| Events []*Method |
| |
| // Generated struct holding variant-agnostic details about protocol. |
| ProtocolDetails name |
| } |
| |
| func (*Protocol) Kind() declKind { |
| return Kinds.Protocol |
| } |
| |
| var _ Kinded = (*Protocol)(nil) |
| var _ namespaced = (*Protocol)(nil) |
| |
| func (p Protocol) HLCPPType() string { |
| return p.HLCPP.String() |
| } |
| |
| func (p Protocol) WireType() string { |
| return p.Wire.String() |
| } |
| |
| func newProtocol(inner protocolInner) Protocol { |
| type kinds []methodKind |
| |
| filterBy := func(kinds kinds) []*Method { |
| var out []*Method |
| for i := 0; i < len(inner.Methods); i++ { |
| m := &inner.Methods[i] |
| k := m.methodKind() |
| for _, want := range kinds { |
| if want == k { |
| out = append(out, m) |
| } |
| } |
| } |
| return out |
| } |
| |
| return Protocol{ |
| protocolInner: inner, |
| OneWayMethods: filterBy(kinds{oneWayMethod}), |
| TwoWayMethods: filterBy(kinds{twoWayMethod}), |
| ClientMethods: filterBy(kinds{oneWayMethod, twoWayMethod}), |
| Events: filterBy(kinds{eventMethod}), |
| ProtocolDetails: makeName("fidl::internal::ProtocolDetails").template(inner.Wire), |
| } |
| } |
| |
| type argsWrapper []Parameter |
| |
| // TODO(fxb/7704): We should be able to remove as we align with args with struct |
| // representation. |
| func (args argsWrapper) isResource() bool { |
| for _, arg := range args { |
| if arg.Type.IsResource { |
| return true |
| } |
| } |
| return false |
| } |
| |
| type messageInner struct { |
| TypeShapeV1 TypeShape |
| TypeShapeV2 TypeShape |
| HlCodingTable name |
| WireCodingTable name |
| } |
| |
| // message contains lower level wire-format information about a request/response |
| // message. |
| // message should be created using newMessage. |
| type message struct { |
| messageInner |
| IsResource bool |
| ClientAllocationV1 allocation |
| ClientAllocationV2 allocation |
| ServerAllocationV1 allocation |
| ServerAllocationV2 allocation |
| } |
| |
| // methodContext indicates where the request/response is used. |
| // The allocation strategies differ for client and server contexts, in LLCPP. |
| type methodContext int |
| |
| const ( |
| _ methodContext = iota |
| clientContext |
| serverContext |
| ) |
| |
| type boundednessQuery func(methodContext, fidlgen.Strictness) boundedness |
| |
| func newMessage(inner messageInner, args []Parameter, wire wireTypeNames, |
| direction messageDirection) message { |
| return message{ |
| messageInner: inner, |
| IsResource: argsWrapper(args).isResource(), |
| ClientAllocationV1: computeAllocation( |
| inner.TypeShapeV1.MaxTotalSize(), |
| direction.queryBoundedness(clientContext, inner.TypeShapeV1.HasFlexibleEnvelope)), |
| ClientAllocationV2: computeAllocation( |
| inner.TypeShapeV2.MaxTotalSize(), |
| direction.queryBoundedness(clientContext, inner.TypeShapeV2.HasFlexibleEnvelope)), |
| ServerAllocationV1: computeAllocation( |
| inner.TypeShapeV1.MaxTotalSize(), |
| direction.queryBoundedness(serverContext, inner.TypeShapeV1.HasFlexibleEnvelope)), |
| ServerAllocationV2: computeAllocation( |
| inner.TypeShapeV2.MaxTotalSize(), |
| direction.queryBoundedness(serverContext, inner.TypeShapeV2.HasFlexibleEnvelope)), |
| } |
| } |
| |
| type wireMethod struct { |
| WireCompleterAlias name |
| WireCompleter name |
| WireCompleterBase name |
| WireMethodTypes name |
| WireRequest name |
| WireRequestView name |
| WireRequestViewAlias name |
| WireResponse name |
| WireResponseContext name |
| WireResult name |
| WireUnownedResult name |
| } |
| |
| func newWireMethod(name string, wireTypes wireTypeNames, protocolMarker name, methodMarker name) wireMethod { |
| s := wireTypes.WireServer.nest(name) |
| return wireMethod{ |
| WireCompleterAlias: s.appendName("Completer"), |
| WireCompleter: WireCompleter.template(methodMarker), |
| WireCompleterBase: WireCompleterBase.template(methodMarker), |
| WireMethodTypes: WireMethodTypes.template(methodMarker), |
| WireRequest: WireRequest.template(methodMarker), |
| WireRequestView: WireRequestView.template(methodMarker), |
| WireRequestViewAlias: s.appendName("RequestView"), |
| WireResponse: WireResponse.template(methodMarker), |
| WireResponseContext: WireResponseContext.template(methodMarker), |
| WireResult: WireResult.template(methodMarker), |
| WireUnownedResult: WireUnownedResult.template(methodMarker), |
| } |
| } |
| |
| // methodInner contains information about a Method that should be filled out by |
| // the compiler. |
| type methodInner struct { |
| protocolName nameVariants |
| Marker nameVariants |
| wireMethod |
| baseCodingTableName string |
| requestTypeShapeV1 TypeShape |
| requestTypeShapeV2 TypeShape |
| responseTypeShapeV1 TypeShape |
| responseTypeShapeV2 TypeShape |
| |
| Attributes |
| |
| // FullyQualifiedName is the fully qualified name as defined by |
| // https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0043_documentation_comment_format?hl=en#fully-qualified-names |
| FullyQualifiedName string |
| |
| nameVariants |
| Ordinal uint64 |
| HasRequest bool |
| RequestArgs []Parameter |
| RequestAnonymousChildren []ScopedLayout |
| HasResponse bool |
| ResponseArgs []Parameter |
| ResponseAnonymousChildren []ScopedLayout |
| Transitional bool |
| Result *Result |
| } |
| |
| // Method should be created using newMethod. |
| type Method struct { |
| methodInner |
| OrdinalName nameVariants |
| Request message |
| Response message |
| CallbackType *nameVariants |
| ResponseHandlerType string |
| ResponderType string |
| // Protocol is a reference to the containing protocol, for the |
| // convenience of golang templates. |
| Protocol *Protocol |
| } |
| |
| func (m Method) WireRequestViewArg() string { |
| return m.appendName("RequestView").Name() |
| } |
| |
| func (m Method) WireCompleterArg() string { |
| return m.appendName("Completer").nest("Sync").Name() |
| } |
| |
| // CtsMethodAnnotation generates a comment containing information about the FIDL |
| // method that is covered by the C++ generated method. It is primarily meant |
| // to be parsed by machines, but can serve as human readable documentation too. |
| // For more information see fxbug.dev/84332. |
| func (m Method) CtsMethodAnnotation() string { |
| // If the formatting of this comment needs to change, it should be done |
| // with consulting the CTS team. |
| return "// cts-coverage-fidl-name:" + m.FullyQualifiedName |
| } |
| |
| type messageDirection int |
| |
| const ( |
| _ messageDirection = iota |
| messageDirectionRequest |
| messageDirectionResponse |
| ) |
| |
| // Compute boundedness based on client/server, request/response, and strictness. |
| func (d messageDirection) queryBoundedness(c methodContext, hasFlexibleEnvelope bool) boundedness { |
| switch d { |
| case messageDirectionRequest: |
| if c == clientContext { |
| // Allocation is bounded when sending request from a client. |
| return boundednessBounded |
| } else { |
| return boundedness(!hasFlexibleEnvelope) |
| } |
| case messageDirectionResponse: |
| if c == serverContext { |
| // Allocation is bounded when sending response from a server. |
| return boundednessBounded |
| } else { |
| return boundedness(!hasFlexibleEnvelope) |
| } |
| } |
| panic(fmt.Sprintf("unexpected message direction: %v", d)) |
| } |
| |
| func newMethod(inner methodInner, hl hlMessagingDetails, wire wireTypeNames) Method { |
| hlCodingTableBase := hl.ProtocolMarker.Namespace().append("_internal").member(inner.baseCodingTableName) |
| wireCodingTableBase := wire.WireProtocolMarker.Namespace().member(inner.baseCodingTableName) |
| |
| hlRequestCodingTable := hlCodingTableBase.appendName("RequestMessageTable") |
| wireRequestCodingTable := wireCodingTableBase.appendName("RequestMessageTable") |
| hlResponseCodingTable := hlCodingTableBase.appendName("ResponseMessageTable") |
| wireResponseCodingTable := wireCodingTableBase.appendName("ResponseMessageTable") |
| if !inner.HasRequest { |
| hlResponseCodingTable = hlCodingTableBase.appendName("EventMessageTable") |
| wireResponseCodingTable = wireCodingTableBase.appendName("EventMessageTable") |
| } |
| |
| var callbackType *nameVariants = nil |
| if inner.HasResponse { |
| callbackName := inner.appendName("Callback") |
| callbackType = &callbackName |
| } |
| ordinalName := fmt.Sprintf("k%s_%s_Ordinal", |
| inner.protocolName.HLCPP.Name(), inner.HLCPP.Name()) |
| |
| m := Method{ |
| methodInner: inner, |
| OrdinalName: nameVariants{ |
| HLCPP: inner.protocolName.HLCPP.Namespace().append("internal").member(ordinalName), |
| Wire: inner.protocolName.Wire.Namespace().member(ordinalName), |
| }, |
| Request: newMessage(messageInner{ |
| TypeShapeV1: inner.requestTypeShapeV1, |
| TypeShapeV2: inner.requestTypeShapeV2, |
| HlCodingTable: hlRequestCodingTable, |
| WireCodingTable: wireRequestCodingTable, |
| }, inner.RequestArgs, wire, messageDirectionRequest), |
| Response: newMessage(messageInner{ |
| TypeShapeV1: inner.responseTypeShapeV1, |
| TypeShapeV2: inner.responseTypeShapeV2, |
| HlCodingTable: hlResponseCodingTable, |
| WireCodingTable: wireResponseCodingTable, |
| }, inner.ResponseArgs, wire, messageDirectionResponse), |
| CallbackType: callbackType, |
| ResponseHandlerType: fmt.Sprintf("%s_%s_ResponseHandler", |
| inner.protocolName.HLCPP.Name(), inner.HLCPP.Name()), |
| ResponderType: fmt.Sprintf("%s_%s_Responder", |
| inner.protocolName.HLCPP.Name(), inner.HLCPP.Name()), |
| Protocol: nil, |
| } |
| return m |
| } |
| |
| type methodKind int |
| |
| const ( |
| oneWayMethod = methodKind(iota) |
| twoWayMethod |
| eventMethod |
| ) |
| |
| func (m *Method) methodKind() methodKind { |
| if m.HasRequest { |
| if m.HasResponse { |
| return twoWayMethod |
| } |
| return oneWayMethod |
| } |
| if !m.HasResponse { |
| panic("A method should have at least either a request or a response") |
| } |
| return eventMethod |
| } |
| |
| func (m *Method) CallbackWrapper() string { |
| return "fit::function" |
| } |
| |
| type Parameter struct { |
| nameVariants |
| Type Type |
| OffsetV1 int |
| OffsetV2 int |
| HandleInformation *HandleInformation |
| } |
| |
| func (p Parameter) NameAndType() (string, Type) { |
| return p.Name(), p.Type |
| } |
| |
| func anyEventHasFlexibleEnvelope(methods []Method) bool { |
| for _, m := range methods { |
| if m.Response.TypeShapeV1.HasFlexibleEnvelope != m.Response.TypeShapeV2.HasFlexibleEnvelope { |
| panic("expected type shape v1 and v2 flexible envelope values to be identical") |
| } |
| if !m.HasRequest && m.HasResponse && m.Response.TypeShapeV1.HasFlexibleEnvelope { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (c *compiler) compileProtocol(p fidlgen.Protocol) *Protocol { |
| protocolName := c.compileNameVariants(p.Name) |
| codingTableName := codingTableName(p.Name) |
| hlMessaging := compileHlMessagingDetails(protocolName) |
| wireTypeNames := newWireTypeNames(protocolName) |
| |
| methods := []Method{} |
| for _, v := range p.Methods { |
| name := methodNameContext.transform(v.Name) |
| |
| var result *Result |
| if v.HasError { |
| result = c.resultForUnion[v.ResultType.Identifier] |
| } |
| |
| methodMarker := protocolName.nest(name.Wire.Name()) |
| |
| var requestChildren []ScopedLayout |
| var requestTypeShapeV1 fidlgen.TypeShape |
| var requestTypeShapeV2 fidlgen.TypeShape |
| var requestPayloadStruct fidlgen.Struct |
| if v.RequestPayload != nil { |
| requestTypeShapeV1 = v.RequestPayload.TypeShapeV1 |
| requestTypeShapeV2 = v.RequestPayload.TypeShapeV2 |
| if val, ok := c.requestResponsePayload[v.RequestPayload.Identifier]; ok { |
| requestPayloadStruct = val |
| requestChildren = c.anonymousChildren[toKey(val.NamingContext)] |
| } |
| } |
| if v.HasRequest { |
| requestTypeShapeV1.InlineSize += kMessageHeaderSize |
| requestTypeShapeV2.InlineSize += kMessageHeaderSize |
| } |
| |
| var responseChildren []ScopedLayout |
| var responseTypeShapeV1 fidlgen.TypeShape |
| var responseTypeShapeV2 fidlgen.TypeShape |
| var responsePayloadStruct fidlgen.Struct |
| if v.ResponsePayload != nil { |
| responseTypeShapeV1 = v.ResponsePayload.TypeShapeV1 |
| responseTypeShapeV2 = v.ResponsePayload.TypeShapeV2 |
| if val, ok := c.requestResponsePayload[v.ResponsePayload.Identifier]; ok { |
| responsePayloadStruct = val |
| responseChildren = c.anonymousChildren[toKey(val.NamingContext)] |
| } |
| } |
| if v.HasResponse { |
| responseTypeShapeV1.InlineSize += kMessageHeaderSize |
| responseTypeShapeV2.InlineSize += kMessageHeaderSize |
| } |
| |
| method := newMethod(methodInner{ |
| nameVariants: name, |
| protocolName: protocolName, |
| // Using the raw identifier v.Name instead of the name after |
| // reserved words logic, since that's the behavior in fidlc. |
| baseCodingTableName: codingTableName + string(v.Name), |
| Marker: methodMarker, |
| requestTypeShapeV1: TypeShape{requestTypeShapeV1}, |
| requestTypeShapeV2: TypeShape{requestTypeShapeV2}, |
| responseTypeShapeV1: TypeShape{responseTypeShapeV1}, |
| responseTypeShapeV2: TypeShape{responseTypeShapeV2}, |
| wireMethod: newWireMethod(name.Wire.Name(), wireTypeNames, protocolName.Wire, methodMarker.Wire), |
| Attributes: Attributes{v.Attributes}, |
| // TODO(fxbug.dev/84834): Use the functionality in //tools/fidl/lib/fidlgen/identifiers.go |
| FullyQualifiedName: fmt.Sprintf("%s.%s", p.Name, v.Name), |
| Ordinal: v.Ordinal, |
| HasRequest: v.HasRequest, |
| RequestArgs: c.compileParameterArray(requestPayloadStruct), |
| RequestAnonymousChildren: requestChildren, |
| HasResponse: v.HasResponse, |
| ResponseArgs: c.compileParameterArray(responsePayloadStruct), |
| ResponseAnonymousChildren: responseChildren, |
| Transitional: v.IsTransitional(), |
| Result: result, |
| }, hlMessaging, wireTypeNames) |
| methods = append(methods, method) |
| } |
| |
| var maxResponseSizeV1 int |
| var maxResponseSizeV2 int |
| for _, method := range methods { |
| if size := method.responseTypeShapeV1.MaxTotalSize(); size > maxResponseSizeV1 { |
| maxResponseSizeV1 = size |
| } |
| if size := method.responseTypeShapeV2.MaxTotalSize(); size > maxResponseSizeV2 { |
| maxResponseSizeV2 = size |
| } |
| } |
| |
| fuzzingName := strings.ReplaceAll(strings.ReplaceAll(string(p.Name), ".", "_"), "/", "_") |
| r := newProtocol(protocolInner{ |
| Attributes: Attributes{p.Attributes}, |
| nameVariants: protocolName, |
| hlMessaging: hlMessaging, |
| wireTypeNames: wireTypeNames, |
| DiscoverableName: p.GetServiceName(), |
| SyncEventAllocationV1: computeAllocation( |
| maxResponseSizeV1, messageDirectionResponse.queryBoundedness( |
| clientContext, anyEventHasFlexibleEnvelope(methods))), |
| SyncEventAllocationV2: computeAllocation( |
| maxResponseSizeV2, messageDirectionResponse.queryBoundedness( |
| clientContext, anyEventHasFlexibleEnvelope(methods))), |
| Methods: methods, |
| FuzzingName: fuzzingName, |
| TestBase: protocolName.appendName("_TestBase").appendNamespace("testing"), |
| }) |
| for i := 0; i < len(methods); i++ { |
| methods[i].Protocol = &r |
| } |
| return &r |
| } |
| |
| func (c *compiler) compileParameterArray(val fidlgen.Struct) []Parameter { |
| var params []Parameter = []Parameter{} |
| for _, v := range val.Members { |
| params = append(params, Parameter{ |
| Type: c.compileType(v.Type), |
| nameVariants: structMemberContext.transform(v.Name), |
| OffsetV1: v.FieldShapeV1.Offset + kMessageHeaderSize, |
| OffsetV2: v.FieldShapeV2.Offset + kMessageHeaderSize, |
| HandleInformation: c.fieldHandleInformation(&v.Type), |
| }) |
| } |
| return params |
| } |
| |
| // |
| // Functions for calculating message buffer size bounds |
| // |
| |
| func fidlAlign(size int) int { |
| return (size + 7) & ^7 |
| } |
| |
| type boundedness bool |
| |
| const ( |
| boundednessBounded = true |
| boundednessUnbounded = false |
| ) |
| |
| // This value needs to be kept in sync with the one defined in |
| // zircon/system/ulib/fidl/include/lib/fidl/llcpp/sync_call.h |
| const llcppMaxStackAllocSize = 512 |
| const channelMaxMessageSize = 65536 |
| |
| // allocation describes the allocation strategy of some operation, such as |
| // sending requests, receiving responses, or handling events. Note that the |
| // allocation strategy may depend on client/server context, direction of the |
| // message, and the content/shape of the message, as we make optimizations. |
| type allocation struct { |
| IsStack bool |
| Size int |
| |
| bufferType bufferType |
| size string |
| } |
| |
| func (alloc allocation) BackingBufferType() string { |
| switch alloc.bufferType { |
| case inlineBuffer: |
| return fmt.Sprintf("::fidl::internal::InlineMessageBuffer<%s>", alloc.size) |
| case boxedBuffer: |
| return fmt.Sprintf("::fidl::internal::BoxedMessageBuffer<%s>", alloc.size) |
| } |
| panic(fmt.Sprintf("unexpected buffer type: %v", alloc.bufferType)) |
| } |
| |
| type bufferType int |
| |
| const ( |
| _ bufferType = iota |
| inlineBuffer |
| boxedBuffer |
| ) |
| |
| func computeAllocation(maxTotalSize int, boundedness boundedness) allocation { |
| var sizeString string |
| var size int |
| if boundedness == boundednessUnbounded || maxTotalSize > channelMaxMessageSize { |
| sizeString = "ZX_CHANNEL_MAX_MSG_BYTES" |
| size = channelMaxMessageSize |
| } else { |
| size = maxTotalSize |
| sizeString = fmt.Sprintf("%d", size) |
| } |
| |
| if size > llcppMaxStackAllocSize { |
| return allocation{ |
| IsStack: false, |
| Size: 0, |
| bufferType: boxedBuffer, |
| size: sizeString, |
| } |
| } else { |
| return allocation{ |
| IsStack: true, |
| Size: size, |
| bufferType: inlineBuffer, |
| size: sizeString, |
| } |
| } |
| } |