# 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 "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
