RFC-0076: FIDL API Summaries | |
---|---|
Status | Accepted |
Areas |
|
Description | Presents a format for a human-readable FIDL API surface. |
Issues | |
Gerrit change | |
Authors | |
Reviewers | |
Date submitted (year-month-day) | 2021-03-16 |
Date reviewed (year-month-day) | 2021-03-16 |
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.
Amendment (Aug 2022). This RFC describes a human-readable text format with each API element on a single line. During implementation, a JSON format containing the same information was added (https://fxrev.dev/480357). Unlike the text format, the JSON format can be parsed back into Go data structures, which is particularly useful for fidl_api_diff. Since only the JSON format is used today, we have removed the text format to ease maintenance.
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.
library fuchsia.accessibility.gesture;
/// Maximum size of a returned utterance.
const uint64 MAX_UTTERANCE_SIZE = 16384;
/// Gesture types that accessibility offers to a UI component for listening.
enum Type {
THREE_FINGER_SWIPE_UP = 1;
THREE_FINGER_SWIPE_DOWN = 2;
THREE_FINGER_SWIPE_RIGHT = 3;
THREE_FINGER_SWIPE_LEFT = 4;
};
/// An interface to listen for accessibility gestures.
protocol Listener {
/// When accessibility services detect a gesture, the listener is informed
/// of which gesture was performed.
OnGesture(Type gesture_type) -> (bool handled, string:MAX_UTTERANCE_SIZE? utterance);
};
/// An interface for registering a listener of accessibility gestures.
[Discoverable]
protocol ListenerRegistry {
/// A UI registers itself to start listening for accessibility gestures
/// through `listener`.
Register(Listener listener) -> ();
};
An API summary of the above library looks like this:
protocol/member fuchsia.accessibility.gesture/Listener.OnGesture(fuchsia.accessibility.gesture/Type gesture_type) -> (bool handled,string:16384? utterance)
protocol fuchsia.accessibility.gesture/Listener
protocol/member fuchsia.accessibility.gesture/ListenerRegistry.Register(fuchsia.accessibility.gesture/Listener listener) -> ()
protocol fuchsia.accessibility.gesture/ListenerRegistry
const fuchsia.accessibility.gesture/MAX_UTTERANCE_SIZE uint64 16384
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_DOWN 2
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_LEFT 4
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_RIGHT 3
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_UP 1
strict enum fuchsia.accessibility.gesture/Type uint32
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 namedfidl.api_summary
, the following command line extracts only the API surface for the protocol:cat fidl.api_summary | grep "fuchsia.accessibility.gesture/ListenerRegistry"
It is similarly easy to extract methods only:
cat fidl.api_summary \ | grep "fuchsia.accessibility.gesture/ListenerRegistry" \ | grep "protocol/member"
A rudimentary API surface diff can be generated by:
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
anddiff
.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:
library fuchsia.accessibility.gesture;
enum Type { THREE_FINGER_SWIPE_UP = 1; };
protocol Listener {
OnGesture(Type gesture_type);
};
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:
fuchsia.accessibility.gesture/Listener.OnGesture
fuchsia.accessibility.gesture/Listener
fuchsia.accessibility.gesture/ListenerRegistry.Register
fuchsia.accessibility.gesture/ListenerRegistry
fuchsia.accessibility.gesture/MAX_UTTERANCE_SIZE
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_DOWN
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_LEFT
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_RIGHT
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_UP
fuchsia.accessibility.gesture/Type
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.
summary ::= declaration_list
declaration_list ::= declaration
| declaration "\n" declaration_list
declaration ::= library
| const
| bits
| bits_member
| enum
| enum_member
| struct
| struct_member
| union
| union_member
| protocol
| protocol_member
| alias
alias ::= "alias" fqn
bits ::= strictness "bits" fqn fp
bits_member ::= "bits/member" fqn
const ::= "const" fqn d fv
enum ::= strictness "enum" ft
enum_member ::= "enum/member" fqn fv
library ::= "library" fqn
protocol ::= "protocol" fqn
protocol_member ::= "protocol/member" fqn d
struct ::= resourceness "struct" fqn
struct_member ::= "struct/member" fqn ft [ fv ]
union ::= strictness "union" fqn
union_member ::= "union/member" fqn
resourceness ::= "" | "resource"
strictness ::= "flexible" | "strict"
d ::= <FIDL protocol member type signature>
fp ::= <FIDL primitive type>
fqn ::= <FIDL identifier>
ft ::= <FIDL type>
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.