| # 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`, `kotlin-reflect` and `kotlin-gradle-plugin-idea` 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 `Test: public API binary compatibility validator[ w/Native], overwrite results`, 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 |