{% set rfcid = “RFC-0076” %} {% include “docs/contribute/governance/rfcs/_common/_rfc_header.md” %}

{{ rfc.name }}: {{ rfc.title }}

Summary

Presents a summarization approach to describe FIDL API surface, with a human-readable format as the first output, and proposes to leverage this summarization to identify API changes to FIDL library in the Fuchsia Source Tree.

Note: The format presented in this RFC at this time aligns to the current syntax of FIDL. There is active and ongoing work to update the syntax, and the tool output will be similarly updated (before it is used to automatically detect backwards-incompatible changes via API hashing). See: fxbug.dev/72411.

Motivation

At the time of this writing, multiple efforts have been started on the Fuchsia project with a common goal of tracking changes to the platform’s API surface. Once complete, the collective result will enable us to use versioning to decouple the platform development from the library versions used by the SDK consumers.

Specifically in the domain of FIDL, there is a need for a human-readable representation of the API surface of a FIDL library. This representation, from here on called a “summary” can be used in multiple ways:

  • As a human-friendly inventory of the API offered by a FIDL library.

    Similar inventories are being kept by other software producing API surfaces, such as go. This allows attributing a version to the specific API summary, in the contexts where such versioning matters.

  • As a basis for detecting backwards-incompatible changes in FIDL APIs.

    API summarization can be used to compute a difference between two API surfaces, yielding an automated way of checking whether one API surface can be evolved into another. This is a precision improvement over the currently used method, in which a stable (called “normalized”) form of the library sources is generated by concatenating in a predictable way all the source files and removing comments and irrelevant spacing.

  • As a building block in other efforts such as the Compatibility Testing Suite (CTS, see RFC-0015) used to detect the tests that need to run after an API change.

    CTS in particular needs to trim down the battery of tests that are run on a platform change. Knowing what changed in an API surface may allow the software to run only the tests that are affected by the change, saving execution time and computing resources.

Introductory example

Consider the following FIDL library definition, taken from fuchsia.accessibility.gesture. The comments have been pared down, but the library is otherwise complete.

  1. library fuchsia.accessibility.gesture;
  2. /// Maximum size of a returned utterance.
  3. const uint64 MAX_UTTERANCE_SIZE = 16384;
  4. /// Gesture types that accessibility offers to a UI component for listening.
  5. enum Type {
  6. THREE_FINGER_SWIPE_UP = 1;
  7. THREE_FINGER_SWIPE_DOWN = 2;
  8. THREE_FINGER_SWIPE_RIGHT = 3;
  9. THREE_FINGER_SWIPE_LEFT = 4;
  10. };
  11. /// An interface to listen for accessibility gestures.
  12. protocol Listener {
  13. /// When accessibility services detect a gesture, the listener is informed
  14. /// of which gesture was performed.
  15. OnGesture(Type gesture_type) -> (bool handled, string:MAX_UTTERANCE_SIZE? utterance);
  16. };
  17. /// An interface for registering a listener of accessibility gestures.
  18. [Discoverable]
  19. protocol ListenerRegistry {
  20. /// A UI registers itself to start listening for accessibility gestures
  21. /// through `listener`.
  22. Register(Listener listener) -> ();
  23. };

An API summary of the above library looks like this:

  1. protocol/member fuchsia.accessibility.gesture/Listener.OnGesture(fuchsia.accessibility.gesture/Type gesture_type) -> (bool handled,string:16384? utterance)
  2. protocol fuchsia.accessibility.gesture/Listener
  3. protocol/member fuchsia.accessibility.gesture/ListenerRegistry.Register(fuchsia.accessibility.gesture/Listener listener) -> ()
  4. protocol fuchsia.accessibility.gesture/ListenerRegistry
  5. const fuchsia.accessibility.gesture/MAX_UTTERANCE_SIZE uint64 16384
  6. enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_DOWN 2
  7. enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_LEFT 4
  8. enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_RIGHT 3
  9. enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_UP 1
  10. strict enum fuchsia.accessibility.gesture/Type uint32
  11. library fuchsia.accessibility.gesture

A few points are of note:

  • Each API element is a single line of text.
  • Each API element is referred to by its fully qualified name.
  • The order in which the API elements appear in the summary is fixed. If the order of declarations in the FIDL file were to be changed this would have no effect on the shape of the API summary.
  • It is easy to use text tools like grep to extract parts of the summary. For example, assuming that the API summary is in the file named fidl.api_summary, the following command line extracts only the API surface for the protocol:

    1. cat fidl.api_summary | grep "fuchsia.accessibility.gesture/ListenerRegistry"

    It is similarly easy to extract methods only:

    1. cat fidl.api_summary \
    2. | grep "fuchsia.accessibility.gesture/ListenerRegistry" \
    3. | grep "protocol/member"
  • A rudimentary API surface diff can be generated by:

    1. diff -u fidl.old.api_summary fidl.new.api_summary

    (assuming that fidl.{old,new}.api_summary contain the original and modified API surfaces respectively)

Requirements

  • The API summary SHOULD be human-readable and amenable to processing with simple tools, like grep and diff.

  • The API summary produced MUST list all and only the elements of the FIDL library which have API surface impact.

Design

The API summary format contains the information about the library which has API impact. This information is defined in the section Definitions: Source Compatibility and Transitionability of RFC-0024, and captured in the FIDL bindings spec. It is really just a subset of the information that is already present in the FIDL IR, but presented in a manner that is easier for humans to read and text utilities to process. Refer to the summary of rules for a comprehensive list.

Every FIDL language construct is covered by the summarization rules.

Each FIDL declaration is named using fully qualified names. So, for example, in the shortened snippet taken from the example above:

  1. library fuchsia.accessibility.gesture;
  2. enum Type { THREE_FINGER_SWIPE_UP = 1; };
  3. protocol Listener {
  4. OnGesture(Type gesture_type);
  5. };

the identifier OnGesture is always referred to as fuchsia.accessibility.gesture/Listener.OnGesture.

The file format is deliberately kept flat for ease of reading and processing. This means that FIDL members (appearing in scopes such as struct or protocol) are listed in separate lines of text. This gives us some leeway to extend the format in the future if needed. For example it would become possible to include future versioning attributes once they are available.

A single API summary file lists all declarations that appear in the entire FIDL library, regardless of how many files the declarations are specified in.

The order in which the declarations appear in the API summary is declaration-order-independent and stable. Related declarations are deliberately kept close for easier post-processing, though this is not a correctness requirement: any declaration-order-independent and stable ordering would have been sufficient.

API Summary ordering

The ordering of declarations is derived from the FIDL AST: declarations are written out consistent with a post-order traversal of the AST, picking identifiers with alphanumerically smaller fully qualified names first when selecting among siblings.

We call this ordering the API Summary ordering.

This approach suggests the following ordering of the declarations in the API summary file:

  1. fuchsia.accessibility.gesture/Listener.OnGesture
  2. fuchsia.accessibility.gesture/Listener
  3. fuchsia.accessibility.gesture/ListenerRegistry.Register
  4. fuchsia.accessibility.gesture/ListenerRegistry
  5. fuchsia.accessibility.gesture/MAX_UTTERANCE_SIZE
  6. fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_DOWN
  7. fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_LEFT
  8. fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_RIGHT
  9. fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_UP
  10. fuchsia.accessibility.gesture/Type
  11. fuchsia.accessibility.gesture

for the example FIDL library given above.

The ordering of declarations in the API summary would have been the same regardless of how the declarations were actually ordered in the .fidl files, including if they were split across multiple files.

API summary declaration schema

A simplified BNF of the API summary file is specified below for reference.

  1. summary ::= declaration_list
  2. declaration_list ::= declaration
  3. | declaration "\n" declaration_list
  4. declaration ::= library
  5. | const
  6. | bits
  7. | bits_member
  8. | enum
  9. | enum_member
  10. | struct
  11. | struct_member
  12. | union
  13. | union_member
  14. | protocol
  15. | protocol_member
  16. | alias
  17. alias ::= "alias" fqn
  18. bits ::= strictness "bits" fqn fp
  19. bits_member ::= "bits/member" fqn
  20. const ::= "const" fqn d fv
  21. enum ::= strictness "enum" ft
  22. enum_member ::= "enum/member" fqn fv
  23. library ::= "library" fqn
  24. protocol ::= "protocol" fqn
  25. protocol_member ::= "protocol/member" fqn d
  26. struct ::= resourceness "struct" fqn
  27. struct_member ::= "struct/member" fqn ft [ fv ]
  28. union ::= strictness "union" fqn
  29. union_member ::= "union/member" fqn
  30. resourceness ::= "" | "resource"
  31. strictness ::= "flexible" | "strict"
  32. d ::= <FIDL protocol member type signature>
  33. fp ::= <FIDL primitive type>
  34. fqn ::= <FIDL identifier>
  35. ft ::= <FIDL type>
  36. fv ::= <FIDL value>

Implementation

The API summarization is implemented by the program fidl_api_summarize. The program takes as input a FIDL IR and outputs the FIDL API summary, with both file names specified as flags. Invokers SHOULD use the extension .api_summary for the output of this program as a matter of convention, although this is by no means a hard requirement.

Performance

fidl_api_summarize is a simple transformation of the FIDL IR file. Spot-checking shows that the program completes its run in ~0.1s when run on a reasonably large library. This means that the program is likely acceptable to be run on every FIDL library as part of the regular build process.

Security considerations

The current implementation of fidl_api_summarize makes no attempt to validate the FIDL IR, and assumes that its input is always generated as a valid output of fidlc. This may make the program amenable to be confused by malformed input, although it is hard to tell whether that can be used as an attack vector to the Fuchsia build process.

Privacy considerations

The information that fidl_api_summarize processes has so far been part of the code repositories that are publicly viewable. It is reasonable to assume that whatever privacy rules apply to its input would also apply to its output.

This means that, if it is ever used to summarize non-public FIDL library code, its output should be held to the same privacy standard as the library code it was used on.

Testing

The program is tested using an extensive library of sample inputs which are processed and compared to local outputs. This ensures consistent results over the lifetime of the Fuchsia code base.

Documentation

The fidl_api_summarize use should be documented with the FIDL help pages on https://fuchsia.dev.

Prior art and references

The go language API regularly produces API surface summaries.