86 lines
3.9 KiB
Markdown
86 lines
3.9 KiB
Markdown
# Kotlin Public API binary compatibility validation tool
|
|
|
|
This tool allows to dump binary API of a Kotlin library that is public in sense of Kotlin visibilities
|
|
and ensure that the public binary API wasn't changed in a way that make this change binary incompatible.
|
|
|
|
## How to run
|
|
|
|
Compile and run tests. `CasesPublicAPITest` verifies the tool itself,
|
|
and `RuntimePublicAPITest` dumps the public API of `kotlin-stdlib`,
|
|
`kotlin-stdlib-jdk7/8`, and `kotlin-reflect` jars,
|
|
which must be built beforehand with gradle. Use `clean assemble` tasks,
|
|
since the incremental compilation currently doesn't produce all the required output.
|
|
|
|
When substantial changes are made to the public API, it may be convenient to overwrite
|
|
the entire dump and compare changes later before committing: pass `-Doverwrite.output=true`
|
|
property to the test to do so.
|
|
|
|
Also you can use shared run configuration "Binary compatibility tests", which also
|
|
overwrites the results when they differ.
|
|
|
|
## What constitutes the public API
|
|
|
|
### Classes
|
|
|
|
A class is considered to be effectively public if all of the following conditions are met:
|
|
|
|
- it has public or protected JVM access (`ACC_PUBLIC` or `ACC_PROTECTED`)
|
|
- it has one of the following visibilities in Kotlin:
|
|
- no visibility (means no Kotlin declaration corresponds to this compiled class)
|
|
- *public*
|
|
- *protected*
|
|
- *internal*, only in case if the class is annotated with `PublishedApi`
|
|
- it isn't a local class
|
|
- it isn't a synthetic class with mappings for `when` tableswitches (`$WhenMappings`)
|
|
- it contains at least one effectively public member, in case if the class corresponds
|
|
to a kotlin *file* with top-level members or a *multifile facade*
|
|
- in case if the class is a member in another class, it is contained in the *effectively public* class
|
|
- in case if the class is a protected member in another class, it is contained in the *non-final* class
|
|
|
|
### Members
|
|
|
|
A member of the class (i.e. a field or a method) is considered to be effectively public
|
|
if all of the following conditions are met:
|
|
|
|
- it has public or protected JVM access (`ACC_PUBLIC` or `ACC_PROTECTED`)
|
|
- it has one of the following visibilities in Kotlin:
|
|
- no visibility (means no Kotlin declaration corresponds to this class member)
|
|
- *public*
|
|
- *protected*
|
|
- *internal*, only in case if the class is annotated with `PublishedApi`
|
|
|
|
> Note that Kotlin visibility of a field exposed by `lateinit` property is the visibility of its setter.
|
|
- in case if the member is protected, it is contained in *non-final* class
|
|
- it isn't a synthetic access method for a private field
|
|
|
|
## What makes an incompatible change to the public binary API
|
|
|
|
### Class changes
|
|
|
|
For a class a binary incompatible change is:
|
|
|
|
- changing the full class name (including package and containing classes)
|
|
- changing the superclass, so that the class no longer has the previous superclass in
|
|
the inheritance chain
|
|
- changing the set of implemented interfaces so that the class
|
|
no longer implements interfaces it had implemented before
|
|
- changing one of the following access flags:
|
|
- `ACC_PUBLIC`, `ACC_PROTECTED`, `ACC_PRIVATE` — lessening the class visibility
|
|
- `ACC_FINAL` — making non-final class final
|
|
- `ACC_ABSTRACT` — making non-abstract class abstract
|
|
- `ACC_INTERFACE` — changing class to interface and vice versa
|
|
- `ACC_ANNOTATION` — changing annotation to interface and vice versa
|
|
|
|
### Class member changes
|
|
|
|
For a class member a binary incompatible change is:
|
|
|
|
- changing its name
|
|
- changing its descriptor (erased return type and parameter types for methods);
|
|
this includes changing field to method and vice versa
|
|
- changing one of the following access flags:
|
|
- `ACC_PUBLIC`, `ACC_PROTECTED`, `ACC_PRIVATE` — lessening the member visibility
|
|
- `ACC_FINAL` — making non-final field or method final
|
|
- `ACC_ABSTRACT` — making non-abstract method abstract
|
|
- `ACC_STATIC` — changing instance member to static and vice versa
|