| // 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. |
| |
| // summarize is a library used to produce a FIDL API summary from the FIDL |
| // intermediate representation (IR) abstract syntax tree. Please refer to the |
| // README.md file in this repository for usage hints. |
| package summarize |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "io" |
| "sort" |
| "strings" |
| |
| "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen" |
| ) |
| |
| // Element describes a single platform surface element. Use Summarize to |
| // convert a FIDL AST into Elements. |
| type Element interface { |
| // Stringer produces a string representation of this Element. |
| fmt.Stringer |
| // Member returns true if the Element is a member of something. |
| Member() bool |
| // Name returns the fully-qualified name of this Element. For example, |
| // "library/protocol.Method". |
| Name() Name |
| // Serialize converts an Element into a serializable representation. |
| Serialize() ElementStr |
| } |
| |
| // All implementers of Element. |
| var _ = []Element{ |
| (*bits)(nil), |
| (*aConst)(nil), |
| (*enum)(nil), |
| (*method)(nil), |
| (*protocol)(nil), |
| (*library)(nil), |
| } |
| |
| type summarizer struct { |
| elements elementSlice |
| } |
| |
| // addElement adds an element for summarization. |
| func (s *summarizer) addElement(e Element) { |
| s.elements = append(s.elements, e) |
| } |
| |
| // Elements obtains the API elements in this summarizer. |
| func (s *summarizer) Elements() []Element { |
| // Ensure predictable ordering of the reported Elements. |
| sort.Sort(s.elements) |
| return s.elements |
| } |
| |
| // addUnions adds the elements corresponding to the FIDL unions. |
| func (s *summarizer) addUnions(unions []fidlgen.Union) { |
| for _, st := range unions { |
| for _, m := range st.Members { |
| if m.Reserved { |
| // Disregard reserved members. |
| continue |
| } |
| s.addElement(newMember( |
| st.Name, m.Name, m.Type, fidlgen.UnionDeclType)) |
| } |
| s.addElement( |
| newAggregateWithStrictness( |
| st.Name, st.Resourceness, fidlgen.UnionDeclType, st.Strictness)) |
| } |
| } |
| |
| // addTables adds the elements corresponding to the FIDL tables. |
| func (s *summarizer) addTables(tables []fidlgen.Table) { |
| for _, st := range tables { |
| for _, m := range st.Members { |
| if m.Reserved { |
| // Disregard reserved members |
| continue |
| } |
| s.addElement(newMember(st.Name, m.Name, m.Type, fidlgen.TableDeclType)) |
| } |
| s.addElement(newAggregate(st.Name, st.Resourceness, fidlgen.TableDeclType)) |
| } |
| } |
| |
| // addStructs adds the elements corresponding to the FIDL structs. |
| func (s *summarizer) addStructs(structs []fidlgen.Struct) { |
| for _, st := range structs { |
| if st.Anonymous { |
| // Disregard anonymous structs for API summarization. |
| continue |
| } |
| for _, m := range st.Members { |
| s.addElement(newMember( |
| st.Name, m.Name, m.Type, fidlgen.StructDeclType)) |
| } |
| s.addElement(newAggregate(st.Name, st.Resourceness, fidlgen.StructDeclType)) |
| } |
| } |
| |
| // Write produces an API summary for the FIDL AST from the root into the supplied |
| // writer. |
| func Write(root fidlgen.Root, out io.Writer) error { |
| for _, e := range Elements(root) { |
| fmt.Fprintf(out, "%v\n", e) |
| } |
| return nil |
| } |
| |
| // WriteJSON produces an API summary for the FIDL AST from the root into the |
| // supplied writer, and formats the data as JSON. |
| func WriteJSON(root fidlgen.Root, out io.Writer) error { |
| e := json.NewEncoder(out) |
| e.SetIndent("", " ") |
| e.SetEscapeHTML(false) |
| return e.Encode(serialize(Elements(root))) |
| } |
| |
| func serialize(e []Element) []ElementStr { |
| var ret []ElementStr |
| for _, l := range e { |
| ret = append(ret, l.Serialize()) |
| } |
| return ret |
| } |
| |
| // Elements returns the API elements found in the supplied AST root in a |
| // canonical ordering. |
| func Elements(root fidlgen.Root) []Element { |
| var s summarizer |
| s.addConsts(root.Consts) |
| s.addBits(root.Bits) |
| s.addEnums(root.Enums) |
| s.addStructs(root.Structs) |
| s.addTables(root.Tables) |
| s.addUnions(root.Unions) |
| s.addProtocols(root.Protocols) |
| s.addElement(library{r: root}) |
| return s.Elements() |
| } |
| |
| func elementCountToString(ec *int) string { |
| if ec != nil { |
| return fmt.Sprintf(":%d", *ec) |
| } else { |
| return "" |
| } |
| } |
| |
| func nullableToString(n bool) string { |
| if n { |
| return "?" |
| } else { |
| return "" |
| } |
| } |
| |
| // fidlNestedToString prints the FIDL type of a sequence or aggregate, assuming |
| // that t is indeed such a type. |
| func fidlNestedToString(t fidlgen.Type) Decl { |
| return Decl(fmt.Sprintf("%v<%v>%v%v", |
| // Assumes t.Kind is one of the sequential types. |
| t.Kind, |
| fidlTypeString(*t.ElementType), |
| elementCountToString(t.ElementCount), |
| nullableToString(t.Nullable))) |
| } |
| |
| // fidlTypeString converts the FIDL type declaration into a string. |
| func fidlTypeString(t fidlgen.Type) Decl { |
| n := nullableToString(t.Nullable) |
| switch t.Kind { |
| case fidlgen.PrimitiveType: |
| return Decl(t.PrimitiveSubtype) |
| case fidlgen.StringType: |
| return Decl(fmt.Sprintf("%s%s%s", |
| t.Kind, elementCountToString(t.ElementCount), n)) |
| case fidlgen.ArrayType, fidlgen.VectorType: |
| return fidlNestedToString(t) |
| case fidlgen.HandleType: |
| switch t.HandleSubtype { |
| case fidlgen.Handle: |
| return Decl(fmt.Sprintf("handle%v", n)) |
| default: |
| return Decl(fmt.Sprintf( |
| "zx/handle:zx/obj_type.%v%v", strings.ToUpper(string(t.HandleSubtype)), n)) |
| } |
| case fidlgen.IdentifierType: |
| return Decl(fmt.Sprintf("%v%v", string(t.Identifier), n)) |
| case fidlgen.RequestType: |
| return Decl(fmt.Sprintf("request<%v>%v", string(t.RequestSubtype), n)) |
| default: |
| return "<not implemented>" |
| } |
| } |