0% found this document useful (0 votes)
4 views

The Java Virtual Machine & the Kotlin Compiler

Uploaded by

elan.ks786
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

The Java Virtual Machine & the Kotlin Compiler

Uploaded by

elan.ks786
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 103

Kotlin

The Java
Virtual Machine
& the Kotlin
Compiler
@kotlin | Developed by JetBrains
The Java language

● Was created in 1995.

● Is an OOP language with strong static typing.

● Has Just-in-time (JIT) compilation.

● Uses the Java Virtual Machine (JVM).

● Has a garbage collector, meaning you can allocate memory and it will be freed
automatically.
Compilation process – Java vs C
Java bytecode
public class examples/Main {

public <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lorg/examples/Main; L0 L1 0
public class Main {
MAXSTACK = 1
public static void main(String[] args) { MAXLOCALS = 1
System.out.print("Hello, World!");
} public static main([Ljava/lang/String;)V
} L0
LINENUMBER 5 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hello, World!"
ICONST_0
ANEWARRAY java/lang/Object
INVOKEVIRTUAL java/io/PrintStream.print
(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
POP
L1
LINENUMBER 6 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
MAXSTACK = 3
MAXLOCALS = 1
}
The JVM under the hood

JVM

Class loading
services Interpreter

JIT-compiler

.сlass files Memory


with management bytecode -> machine code
bytecodes (heap, GC)
translation services
Memory organisation

JVM memory is divided into two parts:


● Static memory, or non-heap memory, is created when the JVM starts, and it primarily
stores class structures, fields, method data, and the code for methods and
constructors.
○ Loaded classes
○ Stacks of all threads
○ Service memory for the JVM itself
○ Small – roughly 1024 KB for stacks
● Heap memory is the runtime data area shared among all JVM threads, and it is used
to allocate memory for all Java objects.
○ All objects created during a program’s execution
○ Large – from several MB up to several TB
Weak generational hypothesis

According to the weak generational hypothesis,


objects tend to die young.

total size of objects


A related assumption is that, as an object lives
longer, the likelihood will be that it will continue to
live increases.

lifetime
Generations

The memory of a program can be divided into two generations:

● Young
● Old

Garbage collection (GC) can be similarly divided:


● Small GC clears only objects from young generation
● Full GC clears objects from both generations
Dead objects

Stack

Young generation Old generation

A D
C

B E
Serial garbage collector
The first garbage collector created was the serial garbage collector, which is single-threaded, and the
parallel and CMS garbage collectors are based on it.

● In the serial garbage collector, the heap is divided into 4 areas:


○ Eden – Roughly 8/10 of the young generation
○ Survivor 0 – Roughly 1/10 of the young generation
○ Survivor 1 – Roughly 1/10 of the young generation
○ Tenured – Roughly 2/3 of the heap

● (Almost) All objects are created in the Eden area.

Young generation Old generation

Eden S0 S1 Tenured
How garbage collection works
Current state:

No available space in Eden

Before GC:

Memory to free, dead objects

Surviving objects

After GC:
How garbage collection works
Current state:

No available space in Eden

Before GC:

After GC:
How garbage collection works
Current state:

No available space in Eden

Before GC:

After GC:
How garbage collection works
Current state:

No available space in Eden

Before GC:

After GC:

Not enough space to move objects from Eden to Survivor 1


Just-in-time compilation

● Program profiling occurs at runtime.

● Pieces of code are compiled for a specific platform to optimize the execution time.

Interpreting a command is much slower than executing it directly on the processor.

Why, then, do we need the interpreter?


Just-in-time compilation

Why, then, do we need the interpreter?

Interpreter JIT-compiler

● Starts working almost instantly. ● Kicks in after a long delay (needs time

● The performance of the executable


v for optimizations).

code is poor. s ● The performance of the executable


(compiled) code is high.
Just-in-time compilation

What sorts of JIT code are worth compiling?

Code that will take a long time to run or code that runs frequently, because the
compilation overhead will be covered by the profit from having optimized execution.
Just-in-time compilation

How can we understand which pieces of code will take


a long time to execute?

Some guy named Alan Turing said it is impossible.

But empirically it is possible. If a piece of code took a


long enough time to execute once, then most likely the
same will be true in the future.
The JVM under the hood

Why does the compiler need to access the interpreter?

JVM

Class loading
services Interpreter

JIT-compiler

.сlass files Memory


with management bytecode -> machine code
bytecodes (heap, GC)
translation services
The JVM under the hood

Why does the compiler need to access the interpreter?

class PiUtils {
private static final double PI = 3.141592653589;

public static double getPiSquared() {


return PI * PI;
}
}

public static double getPiSquared() {


return 9.869604401084375;
}
The JVM under the hood

Why does the compiler need to access the interpreter?

class PiUtils {
private static final double PI = 4;

public static double getPiSquared() {


return PI * PI;
}
}

Reflection

public static double getPiSquared() {


return 9.869604401084375;
}
The Kotlin language

● Can be compiled into JVM bytecode.

● Is interoperable with Java.

○ Can work in Java projects.

○ Can use Java libraries.

● Is safe and concise.

● Provides the ability to integrate into the compilation process (compiler plugins).

● Is the main development language for Android applications, according to Google.


Kotlin compiler

Parser Frontend Backend

.kt files
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

JVM JavaScript Native Other


(WASM, Python,
etc.?)
Parsing in a nutshell
Source List of
'2 * 7 + 3' code '2' '*' '7' '+' '3' tokens

Su CST Su AST
m m
left operand right operand

Product '+' '3' Product 3

left operand right operand

'2' '*' '7' 2 7


Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

Splitting the program into tokens JVM JavaScript Native Other


(WASM, Python,
(keywords, identifiers, etc.). etc.?)
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

Converting the list of JVM JavaScript Native Other


(WASM, Python,
tokens into CST or AST etc.?)
Kotlin compiler: PSI
Kotlin compiler: PSI
fun hello(user: String) = ...

FUN

'fun' '' 'hello' VALUE_PARAMETER_LIST '' '=' '' ...

'(' VALUE_PARAMETER ')'

'user' ':' '' ...

'String'
Kotlin compiler: PSI
fun hello(user: String) = println("Hello, $user")

CALL_EXPRESSION

VALUE_ARGUMENT_LIS
REFERENCE_EXPRESSION
T

VALUE_ARGUMEN
'println' '(' ')'
T

STRING_TEMPLATE

SHORT_STRING_TEMPLATE_ENT
'\"' LITERAL_STRING_TEMPLATE_ENTRY '\"'
RY

'Hello, ' '$' REFERENCE_EXPRESSION

'user'
Kotlin compiler: PSI
fun hello(user: String) = println("Hello, $user")

CALL_EXPRESSION

VALUE_ARGUMENT_LIS
REFERENCE_EXPRESSION
T

VALUE_ARGUMEN
'println' '(' ')'
T

STRING_TEMPLATE

SHORT_STRING_TEMPLATE_ENT
'\"' LITERAL_STRING_TEMPLATE_ENTRY '\"'
RY

'Hello, ' '$' REFERENCE_EXPRESSION

'user'
Kotlin compiler: PSI
fun hello(user: String) = println("Hello, $user")

CALL_EXPRESSION

VALUE_ARGUMENT_LIS
REFERENCE_EXPRESSION
T

VALUE_ARGUMEN
'println' '(' ')'
T

STRING_TEMPLATE

SHORT_STRING_TEMPLATE_ENT
'\"' LITERAL_STRING_TEMPLATE_ENTRY '\"'
RY

'Hello, ' '$' REFERENCE_EXPRESSION

'user'
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

The so-called FIR tree


is being built,. It’s an Other
JVM JavaScript Native
analogue of the PSI, (WASM, Python,
but it’s mutable. etc.?)
FIR? Another tree!
fun hello(user: String) = println("Hello, $user")

SimpleFunction (name = hello)

valueParameters body returnTypeRef

ValueParameter (name = user) ... ResolvedTypeRef(=kotlin/Unit)

returnTypeRef

ResolvedTypeRef(=kotlin/String)
FIR? Another tree!
fun hello(user: String) = println("Hello, $user")

SimpleFunction (name = hello)

valueParameters body returnTypeRef

ValueParameter (name = user) ... ResolvedTypeRef(=kotlin/Unit)

returnTypeRef

ResolvedTypeRef(=kotlin/String)
FIR? Another tree!
fun hello(user: String) = println("Hello, $user")

StringConcatenationCall

argumentList typeRef

ArgumentList ResolvedTypeRef (=kotlin/String)

arguments arguments

ConstExpression (value = "Hello, ", kind = String) FunctionCall

typeRef explictReceiver calleeReference typeRef

ResolvedTypeRef(=kotlin/String) QualifiedAccessExpression ResolvedNameReference (name = toString) ResolvedTypeRef (=kotlin/String)

calleeReference typeRef

ResolvedNameReference (name = user) ResolvedTypeRef (=kotlin/String)


FIR? Another tree!
fun hello(user: String) = println("Hello, $user")

StringConcatenationCall

argumentList typeRef

ArgumentList ResolvedTypeRef (=kotlin/String)

arguments arguments

ConstExpression (value = "Hello, ", kind = String) FunctionCall

typeRef explictReceiver calleeReference typeRef

ResolvedTypeRef(=kotlin/String) QualifiedAccessExpression ResolvedNameReference (name = toString) ResolvedTypeRef (=kotlin/String)

calleeReference typeRef

ResolvedNameReference (name = user) ResolvedTypeRef (=kotlin/String)


FIR? Another tree!
fun hello(user: String) = println("Hello, $user")

StringConcatenationCall

argumentList typeRef

ArgumentList ResolvedTypeRef (=kotlin/String)

arguments arguments

ConstExpression (value = "Hello, ", kind = String) FunctionCall

typeRef explictReceiver calleeReference typeRef

ResolvedTypeRef(=kotlin/String) QualifiedAccessExpression ResolvedNameReference (name = toString) ResolvedTypeRef (=kotlin/String)

calleeReference typeRef

ResolvedNameReference (name = user) ResolvedTypeRef (=kotlin/String)


FIR? Another tree!
fun hello(user: String) = println("Hello, $user")

StringConcatenationCall

argumentList typeRef

ArgumentList ResolvedTypeRef (=kotlin/String)

arguments arguments

ConstExpression (value = "Hello, ", kind = String) FunctionCall

typeRef explictReceiver calleeReference typeRef

ResolvedTypeRef(=kotlin/String) QualifiedAccessExpression ResolvedNameReference (name = toString) ResolvedTypeRef (=kotlin/String)

calleeReference typeRef

ResolvedNameReference (name = user) ResolvedTypeRef (=kotlin/String)


FIR: desugaring

if (b) { when {
println("Hello") b -> println("Hello")
}
}

val <iterator> = list.iterator()


for (s in list) { while (<iterator>.hasNext()) {
println(s) val s = <iterator>.next()
} println(s)
}

val <destruct> = "a" to "b"


val (a, b) = "a" to "b" val a = <destruct>.component1()
val b = <destruct>.component2()
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

Resolving the FQ names


JVM JavaScript Native Other
(WASM, Python,
etc.?)
Kotlin compiler: resolve

fun myFunction() { fun myFunction() {

} }

Library A Library B

Short FQ name: myFunction Short FQ name: myFunction


Resolved FQ name: org.libraryA.myFunction Resolved FQ name: org.libraryB.myFunction
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

Infers all types and


resolves functions Other
JVM JavaScript Native
bodies (WASM, Python,
etc.?)
Kotlin compiler
fun hello(user: String) = println("Hello, $user")

SimpleFirFunction (name = hello) body ...

valueParameters

returnTypeRef ValueParameter (name = user) ConstExpression (value = "Hello, ", kind = String)

returnTypeRef typeRef

ImplicitTypeRef UserTypeRef (="String") ResolvedTypeRef (=kotlin/String)

Type Type
inference resolution

ResolvedTypeRef (=kotlin/Unit) ResolvedTypeRef (=kotlin/String)


Java interoperability: nullability

● Java nullable types in Kotlin

Java sources Kotlin sources

public class Main { var a: ???? = foo()


public static String foo() {
// TODO
}
}
Java interoperability: nullability

● Java nullable types in Kotlin

Java sources Kotlin sources

public class Main { var a: ???? = foo()


public static String foo() {
// TODO String!
}
}
Java interoperability: nullability

● Java nullable types in Kotlin


● String! is the type range: [String..String?]

Java sources Kotlin sources

public class Main { var a: ???? = foo()


public static String foo() {
// TODO String!
}
}
Java interoperability: nullability

● Java nullable types in Kotlin


● Nullability annotations @NotNull and @Nullable

Java sources Kotlin sources

public class Main { var a: String = foo()


@NotNull
public static String foo() {
// TODO
}
}
Java interoperability: collection mapping

● Java collection types in Kotlin

Java sources Kotlin sources

public class Main { var a: ???? = foo()


@NotNull
public static List<@NotNull String> foo() {
// TODO
}
}
Java interoperability: collection mapping

● Java collection types in Kotlin


● (Mutable)List<T> is the type range: [MutableList<T>..List<T>]

Java sources Kotlin sources

public class Main { var a: ???? = foo()


@NotNull
public static List<@NotNull String> foo() {
// TODO (Mutable)List<String>
}
}
Kotlin compiler: control- and data-flow
analysis
● Variable initialization analysis val a: Int
● Return analysis
● Smart cast analysis while(true) {
if (Random.nextBoolean()) {
a = 15
break
Each variable is initialized before being
used. }
}
Each immutable variable is not
reassigned after initialization.
println(a) // It compiles!
Kotlin compiler: control- and data-flow
analysis
● Variable initialization analysis fun bar(): Int {
● Return analysis print("Again")
● Smart cast analysis while (true) {
print(" and again")
}
} // It compiles!
If the return type is not Unit, then the
function won’t return control unless it
returns something. fun baz(): Long {
error("YOLO! :)")
} // It compiles!
Kotlin compiler: control- and data-flow
analysis
● Variable initialization analysis fun Any?.printFirstElement() {

● Return analysis when (this) {

● Smart cast analysis is List<*> -> get(0)


is Iterable<*> -> iterator().next()
}
}
If type check is successful, then the
checked value is automatically casted
fun String?.length(): Int =
to the corresponding type.
if (this == null) 0
else length

fun Int?.isEven(): Boolean =


this != null && this % 2 == 0
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler: control- and data-flow
analysis
interface A {
Enter function bar

fun foo() Enter while loop


}
Evaluate loop
condition
fun bar(x: Any, b: Boolean) { true fals
e
while (true) {
Exit loop block Enter loop block Exit loop
if (b) {
x as A
Exit if Enter if Function call: x.foo()
break
fals
} e Evaluate if condition Exit function bar
true
}
x.foo() x is A Enter if branch Type operator: x as A

}
Jump: break
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

Almost all checks


you've ever seen in Other
JVM JavaScript Native
IntelliJ IDEA. (WASM, Python,
etc.?)
Kotlin compiler: diagnostics
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

On the backend, we
DO NOT resolve, Other
JVM JavaScript Native
but only use the (WASM, Python,
received information etc.?)
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

IR, a representation slightly


different from FIR. Other
JVM JavaScript Native
(WASM, Python,
It is still common for all etc.?)

platforms.
Kotlin compiler: resolved tree

fun hello(user: String) = println("Hello, $user")


Kotlin compiler: resolved tree

public fun hello(user: kotlin.String): kotlin.Unit defined in example


in file Example.kt

fun hello(user: String) = println("Hello, $user")


Kotlin compiler: resolved tree

Reference to value-parameter user: kotlin.String defined in


example.hello

fun hello(user: String) = println("Hello, $user")


Kotlin compiler: resolved tree

Type: kotlin.String

fun hello(user: String) = println("Hello, $user")


Kotlin compiler: resolved tree

Type: kotlin.String

fun hello(user: String) = println("Hello, $user")


Kotlin compiler: resolved tree

Type: kotlin.Unit

fun hello(user: String) = println("Hello, $user")


Kotlin compiler: resolved tree

Call: kotlin.io.println(kotlin.String)

fun hello(user: String) = println("Hello, $user")


IR? Yet another tree!
The code:
// file 'src/kotlin/example.kt'

package helloWorld

fun hello(user: String) = println("Hello, $user")

fun main(args: Array<String>) {


val user = args[0]
hello(user)
}
IR? Yet another tree!
Its IR: FILE fqName:helloWorld fileName:/example.kt

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String) FUN name:main visibility:public modality: FINAL <>(args:kotlin.Array<kotlin.String>)
returnType:kotlin.Unit returnType:kotlin.Unit

BLOCK_BOD BLOCK_BOD
VALUE_PARAMETER name:user index:0 type:kotlin.String VALUE_PARAMETER name:args index:0 type:kotlin.Array<kotlin.String>
Y Y

RETURN type=kotlin.Nothing VAR name:user type:kotlin.String [val]


from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit CALL 'public final fun get (index: kotlin.Int): T of kotlin.Array CALL 'public final fun hello (user: kotlin.String): kotlin.Unit declared
[inline] declared in kotlin.io.ConsoleKt' [operator] declared in kotlin.Array' in helloworld'
type=kotlin.Unit origin=null type kotlin.String origin=null type kotlin.Unit origin=null

message: index:

$this: user:
STRING_CONCATENATION type=kotlin.String
CONST Int type=kotlin.Int value=0

GET_VAR 'val user: kotlin.String [val]


GET_VAR 'args: kotlin.Array<kotlin.String> declared in helloworld.main'
declared in helloworld.main' type=kotlin.String origin=null
CONST String type=kotlin.String value="Hello, " GET VAR 'user: kotlin.String type=kotlin.Array<kotLin.String> origin=null
declared in helloWorld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Back-end intermediate representation: a closer look
fun hello(user: String) = println("Hello, $user")

FUN name:hello visibility:public modality:FINAL <>(user:kotlin.String)


returnType:kotlin.Unit

VALUE_PARAMETER name:user index:0 type:kotlin.String BLOCK_BODY

RETURN type=kotlin.Nothing
from='public final fun hello (user: kotlin.String): kotlin.Unit declared in helloworld'

CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in
kotlin.io.ConsoleKt' type=kotlin.Unit origin=null

message:

STRING_CONCATENATION type=kotlin.String

CONST String type=kotlin.String value="Hello, "


GET VAR 'user: kotlin.String declared in helloworld.hello'
type=kotlin.String origin=null
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

Platform-specific code,
such as bytecode for the Other
JVM JavaScript Native
JVM (WASM, Python,
etc.?)
Kotlin compiler
Parser
Lexer PSI or Lighter AST builder

.kt files
Frontend
Resolutio
Diagnostics Type inference
n

Backend
IR generator IR optimizer

Platform-specific code,
such as bytecode for the Other
JVM JavaScript Native
JVM (WASM, Python,
etc.?)
KLibs

JAR analogues – store a serialized IR for the subsequent use of cross-platform libraries.

JVM code

Kotlin/JVM Classes

Common code KLib

Native
Kotlin/Native
objects

Native code
Compiler plugins

You can extend any compile phase via compiler plugins. To do so, you need to implement
compiler extensions and register them.

To register extensions, you need to inherit from CompilerPluginRegistrar (


ComponentRegistrar for previous versions).

Don’t forget to use supportsK2 = true if you need to support the FIR frontend
Compiler plugins: FIR extensions

To get the full list of the extensions, please see: package org.jetbrains.kotlin.fir.extensions (link).

Consider an example:
/*
* Generates top level class
*
* package foo.bar
*
* public final class MyClass {
* fun foo(): String = "Hello world"
*}
*/
Compiler plugins: FIR extensions

class SimpleClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {


companion object {
// foo.bar.MyClass
val MY_CLASS_ID = ClassId(
FqName.fromSegments(listOf("foo", "bar")),
Name.identifier("MyClass")
)
// foo.bar.MyClass.foo
val FOO_ID = CallableId(MY_CLASS_ID, Name.identifier("foo"))
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
...
}
...
}
Compiler plugins: FIR extensions

class SimpleClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {


companion object {
// foo.bar.MyClass
val MY_CLASS_ID = ClassId(
FqName.fromSegments(listOf("foo", "bar")),
Name.identifier("MyClass")
)
// foo.bar.MyClass.foo
val FOO_ID = CallableId(MY_CLASS_ID, Name.identifier("foo"))
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
...
}
...
}
Compiler plugins: FIR extensions

class SimpleClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {


companion object {
// foo.bar.MyClass
val MY_CLASS_ID = ClassId(
FqName.fromSegments(listOf("foo", "bar")),
Name.identifier("MyClass")
)
// foo.bar.MyClass.foo
val FOO_ID = CallableId(MY_CLASS_ID, Name.identifier("foo"))
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
...
}
...
}
Compiler plugins: FIR extensions

class SimpleClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {


companion object {
// foo.bar.MyClass
val MY_CLASS_ID = ClassId(
FqName.fromSegments(listOf("foo", "bar")),
Name.identifier("MyClass")
)
// foo.bar.MyClass.foo
val FOO_ID = CallableId(MY_CLASS_ID, Name.identifier("foo"))
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
...
}
...
}
Compiler plugins: FIR extensions

class SimpleClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {


companion object {
// foo.bar.MyClass
val MY_CLASS_ID = ClassId(
FqName.fromSegments(listOf("foo", "bar")),
Name.identifier("MyClass")
)
// foo.bar.MyClass.foo
val FOO_ID = CallableId(MY_CLASS_ID, Name.identifier("foo"))
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
...
}
...
}
Compiler plugins: FIR extensions

You use a special key to mark everything generated by the compiler and can transfer any
information between frontend and backend. So it also helps to find the new declaration to
generate it’s IR.

class SimpleClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {

object Key : GeneratedDeclarationKey()

...

}
Compiler plugins: FIR extensions

Don’t forget to register all new declarations.

class SimpleClassGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {


...
override fun getTopLevelClassIds(): Set<ClassId> {
return setOf(MY_CLASS_ID)
}

override fun hasPackage(packageFqName: FqName): Boolean {


return packageFqName == MY_CLASS_ID.packageFqName
}

}
Compiler plugins: IR extensions

Actually, the compiler has only one extension for IRs: IrGenerationExtension.
You just need to implement some transformers and accept them:

class SimpleIrGenerationExtension: IrGenerationExtension {


override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
val transformers = listOf(SimpleIrBodyGenerator(pluginContext))
for (transformer in transformers) {
moduleFragment.acceptChildrenVoid(transformer)
}
}
}

Don’t forget to check the key (use the interestedIn function)!


Compiler plugins: popular plugins

● kotlinx.serialization — Generates visitor code for serializable classes.


● all-open — Marks all classes as open classes.
● kapt — An annotation processor.
● ksp — An API for developing lightweight compiler plugins.
● Jetpack Compose — Generates efficient UI from its declarative description.
● Arrow Meta — Compiler plugin API that empowers all Arrow libraries.
● ...
Thanks!

@kotlin | Developed by JetBrains

You might also like