Lightweight compiler plugin intended for Kotlin/JVM library development and symbol visibility control.

Overview

Restrikt

Gradle Plugin Portal Maven Central License Kotlin

A Kotlin/JVM compiler plugin to restrict symbols access, from external project sources.


This plugin offers two ways to hide symbols:

  • An automatic way, with the automatic hiding of internal symbols ;
  • A manual way, by using two different annotations to hide symbols from either Kotlin or Java sources.

Summary

  1. Dependency
    1. Gradle plugins DSL
    2. apply method
  2. Usage
    1. Plugin Configuration
    2. Internal symbols hiding
    3. Annotations
    4. Important notes
  3. Known issues
  4. How it works
  5. Future plans
  6. Changelog

Dependency

Both compiler plugin and annotations are added to your project's dependencies in the same unique way, as shown below :

Using the Gradle plugin DSL (Gradle 2.1+)

Using Kotlin DSL:

plugins {
    id("com.zwendo.restrikt") version "[latest-version]"
}

Using Groovy DSL:

plugins {
   id 'com.zwendo.restrikt' version '[latest-version]'
}

Using apply method (Gradle prior to 2.1)

Using Kotlin DSL:

buildscript {
   repositories {
      maven {
         url = uri("https://plugins.gradle.org/m2/")
      }
   }

   dependencies {
      classpath("gradle.plugin.com.restrikt:restrikt:[latest-version]")
   }
}

apply(plugin = "com.zwendo.restrikt")

Using Groovy DSL:

buildscript {
   repositories {
      maven {
         url 'https://plugins.gradle.org/m2/'
      }
   }

   dependencies {
      classpath 'gradle.plugin.com.restrikt:restrikt:[latest-version]'
   }
}

apply plugin: 'com.zwendo.restrikt'

Usage

Plugin Configuration

You can configure the plugin using the configuration DSL.

restrikt {
   option = value
   // ...
}

Here are the currently supported default configuration options:

name type default description
automaticInternalHiding boolean true Whether the internal symbols should be automatically hidden.
annotationProcessing boolean true Whether the plugin annotations should be parsed to manually hide symbols.

Moreover, both annotations of the plugin can be individually configured using their own DSL (hideFromKotlin or hideFromJava), with the following configuration options:

name type default description
enabled boolean true Whether the annotation should be processed to hide symbols. (works only if annotationProcessing is true).
keepAnnotation boolean true Whether the annotation should be written to the classfile.
defaultReason string none The default reason written on the annotation if no specific reason is provided.

Internal symbols hiding

Restrikt plugin features automatic hiding of internal symbols in Kotlin sources. At compile time, all symbols with the internal visibility automatically receives the JVM ACC_SYNTHETIC flag, making them invisible to Java sources.

Annotations usage

This plugin provides two annotations intended for symbol access restrictions. These two annotations, namely HideFromJava and HideFromKotlin, are used to hide symbols from Java and Kotlin sources respectively. They are designed to be used in the same way, just by placing the right annotation on the symbol to hide as follows:

@HideFromJava
fun someFunction() { // will be hidden from java sources
   // ...
}

@HideFromKotlin
class SomeClass // will be hidden from kotlin sources

Both annotations also accepts a string parameter to indicate the reason of the restriction. If no message is provided, the default message defined in the plugin configuration will be used instead.

@HideFromKotlin("This class is designed for Java")
class Bar { // will be hidden from kotlin sources
   // ...
}

Important notes

  • All hidden elements will still be accessible at runtime, meaning that already compiled code will still be able to access it ;
  • Symbols hidden from Kotlin will still be accessible at compile-time from Kotlin sources in the same package.

Known issues

Problems listed below are in the process of being resolved. If you encounter an issue that doesn't seems to be in this list, feel free to open an issue for it.


from 2.0.0

With the new way of modifying the compilation, the plugin will run into an error if called to compile a project that meets all the following conditions:

  • Define an inline function that takes a crossinline functional type and returns an anonymous object. The lambda must be captured by the anonymous object ;
  • Define a function calling the previous function with a literal lambda.

These conditions can be illustrated by the following example:

// first requirement
inline fun inlinedAnonymous(crossinline lambda: () -> Unit) = object {

      fun bar() = lambda()

}

// second requirement
fun problem() = inlinedAnonymous {
   /* the lambda inlining causes the error */
}

fun valid(lambda: () -> Unit) = inlinedAnonymous(lambda)

A simple solution (if performances are not a critical issue) is just to remove the inline modifier from the function.

How it works

This sections is intended for curious people and aims at describing the most specific parts of how this project works.

Automatic internal symbols detection

All Kotlin specific informations of a classfile is stored in an annotation (the @Metatdata annotation) placed on the class declaration. At compile-time be able to parse this annotation is the only way to access Kotlin specific data. Thank to the kotlinx-metadata parser library, this parsing is made possible and allows to know at compile-time which symbols are internal in order to hide them.

Java hiding

Like the Kotlin @JvmSynthetic, this annotation induce the generation of the JVM ACC_SYNTHETIC, hiding symbols from Java sources.

Kotlin hiding

To effectively hide elements from Kotlin, the plugin generates on the marked elements the @Deprecated annotation. This annotation used with the DeprecationLevel.HIDDEN level, makes the element invisible to Kotlin sources, but still visible to Java sources.

// Foo.kt
@HideFromKotlin("java only")
class Foo {
    // ...
}

// will be compiled to ...

// Foo.class
@HideFromKotlin
@Deprecated("java only", DeprecationLevel.HIDDEN)
class Foo {
    // ...
}

NOTE: The message of the HideFromKotlin annotation will be transferred to the Deprecated annotation.

Generating the Deprecated annotation or simply using it directly have slightly different outcomes. Indeed, the Deprecated annotation (with HIDDEN level) acts as a flag for the Kotlin compiler. The latter will add the JVM ACC_SYNTHETIC flag for the element in the produced classfile, making it also invisible for Java sources. The hack is that the Kotlin compiler runs before calling the compiler plugin, so when it writes the classfile, the Deprecated annotation is not present meaning that the ACC_SYNTHETIC flag is not set.

Compilation order workaround

The main difficulty while developing Kotlin compiler plugins is that.

  1. Metadata is parsed at the end of the file parsing, meaning that all "Kotlin informations" are known when the entire file has already been defined.
  2. Symbol annotation processing is done after that the involved symbol has been declared, which is problem when it comes to change a symbol modifiers depending on its annotations.

To solve both of these problems, this project uses a singleton representing a context. Each writing instruction is delayed by queueing the associated action (as a () -> Unit lambda) in a list in the context. These actions can reference external values that aren't already resolved until the right symbol is parsed, like demonstrated below (simplified example):

lateinit var isInternal: Boolean // will be filled by an other function

fun functionDeclaration(signature: String, modifiers: Int, /* ... */) {

   Context.queue { // when this lambda will be executed, isInternal value will been set
      val actualModifiers = if (isInternal) modifier or Modifiers.SYNTHETIC else actualModifiers
      writeFunctionDeclaration(signature, actualModifiers, /* ... */)
   }
}

Finally, when the last symbol of the file has been correctly queued, all lambdas are called in the right order, to keep output classfile integrity.

Future Plans

  • Add support for generating annotations on all public (to be able to differentiate internal and public) symbols of a project to simplify Kotlin project obfuscation with ProGuard.

Changelog

2.0.0 - 2022.08.27

  • [Feature] automatic detection and hiding of internal symbols
  • [Feature] HideFromJava annotation to hide symbols from Java sources
  • [Feature] new gradle plugin configuration options for each annotation and internal hiding
You might also like...
Playground project for Koin Koin Compiler - Sandbox

Koin Compiler - Sandbox The goal of Koin compiler & Annotations project is to help declare Koin definition in a very fast and intuitive way, and gener

This repository is part of a Uni-Project to write a complete Compiler for a subset of Java.

Compiler This repository is part of a Uni-Project to write a complete Compiler for a subset of Java. Features error recovery using context sensitive a

A Open GAL compiler based on OpenGAL 0.3.1

A Open GAL compiler based on OpenGAL 0.3.1

A CLI tool to convert multi-module Jetpack Compose compiler metrics into beautiful HTML reports
A CLI tool to convert multi-module Jetpack Compose compiler metrics into beautiful HTML reports

A CLI tool to convert multi-module Jetpack Compose compiler metrics into beautiful HTML reports 1. What are Jetpack Compose compiler metrics? The Comp

Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client
Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client

Practical Kotlin Multiplatform on the Web 본 저장소는 코틀린 멀티플랫폼 기반 웹 프로그래밍 워크숍(강좌)을 위해 작성된 템플릿 프로젝트가 있는 곳입니다. 워크숍 과정에서 코틀린 멀티플랫폼을 기반으로 프론트엔드(front-end)는 Ko

Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client
Create an application with Kotlin/JVM and Kotlin/JS, and explore features around code sharing, serialization, server- and client

Building a Full Stack Web App with Kotlin Multiplatform 본 저장소는 INFCON 2022에서 코틀린 멀티플랫폼 기반 웹 프로그래밍 핸즈온랩을 위해 작성된 템플릿 프로젝트가 있는 곳입니다. 핸즈온 과정에서 코틀린 멀티플랫폼을

AndroRAT is a tool designed to give the control of the android system remotely and retrieve informations from it.
AndroRAT is a tool designed to give the control of the android system remotely and retrieve informations from it.

AndroRAT is a tool designed to give the control of the android system remotely and retrieve informations from it. Androrat is a client/server application developed in Java Android for the client side and the Server is in Python.

Run Kotlin/JS libraries in Kotlin/JVM and Kotlin/Native programs

Zipline This library streamlines using Kotlin/JS libraries from Kotlin/JVM and Kotlin/Native programs. It makes it possible to do continuous deploymen

An under development minecraft plugin (1.8.8) to learning Kotlin language

CorePlus CorePlus is a minecraft plugin coded with Kotlin language. Still under development CorePlus will be an essential for each minecraft servers !

Owner
Lorris Creantor
discord: ZwenDo#4259
Lorris Creantor
An annotation and Kotlin compiler plugin for enforcing a when statement is exhaustive

An annotation and Kotlin compiler plugin for enforcing a when statement is exhaustive

Cash App 468 Jan 4, 2023
A composite Github Action to execute the Kotlin Script with compiler plugin and dependency caching!

Kotlin Script Github Action Kotlin can also be used as a scripting language, which is more safer, concise, and fun to write than bash or python. Githu

Suresh 9 Nov 28, 2022
A Kotlin compiler plugin that allows Java callers to pass in null for default parameters

kotlin-null-defaults (Compiler plugin) (Gradle Plugin) ( Currently pending approval) A Kotlin compiler plugin that allows Java callers to pass in null

Youssef Shoaib 7 Oct 14, 2022
Kstr is a set of helpful methods library for Kotlin intended for make the developer life easier.

Kstr is a set of helpful methods library for Kotlin intended for make the developer life easier. Kstr uses the powerful feature of extension func

Rafael Acioly 0 Nov 3, 2021
A simple example of kotlim compiler plugin with FIR and IR.

A simple Kotlin compiler plugin example This Kotlin compiler plugin generates a top level class: public final class foo.bar.MyClass { fun foo(): S

Anastasiia Birillo 10 Dec 2, 2022
🚟 Lightweight, and simple scheduling library made for Kotlin (JVM)

Haru ?? Lightweight, and simple scheduling library made for Kotlin (JVM) Why did you build this? I built this library as a personal usage library to h

Noel 13 Dec 16, 2022
Minecraft 1.18.2 Backport of Petal, a performance-oriented fork of Purpur intended to increase performance for entity-heavy servers by implementing multi-threaded and asynchronous improvements.

Sakura Performance Minecraft JAR Sakura is a performance-oriented fork of Purpur intended to increase performance for entity-heavy servers by implemen

etil.sol 14 Nov 23, 2022
Koi, a lightweight kotlin library for Android Development.

Koi - A lightweight Kotlin library for Android Koi include many useful extensions and functions, they can help reducing the boilerplate code in Androi

Hello World 514 Nov 29, 2022
An experimental tool for building console UI in Kotlin using the Jetpack Compose compiler/runtime

An experimental tool for building console UI in Kotlin using the Jetpack Compose compiler/runtime

Jake Wharton 1.4k Dec 28, 2022
Build a compiler in Kotlin (based on the original tutorial by Jack Crenshaw)

Let's Build a Compiler Based on the original series "Let’s Build a Compiler!" by Jack Crenshaw. This is an adaptation of the original series to Kotlin

null 2 Oct 9, 2022