blob: dd5471e31b00622d403e46215a29ce8c4f5b02fa [file] [log] [blame]
// 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>"
}
}