SlideShare a Scribd company logo
© Instil Software 2020
Lies Told By The Kotlin
Compiler
Looking under the hood of Kotlin JVM
@GarthGilmour @ryangadams
https://instil.co
Lies Told By The Kotlin Compiler
– Kotlin is an awesome language!
– It provides many wonderful features
– These features don’t exist on the supported
platforms
– We need the compiler to emulate them
– Initially we don’t care how this is done
introducing this talk
why do we need lies?
the lies are a good thing
they create a more productive world
"Being abstract is something profoundly different from
being vague … The purpose of abstraction is not to be
vague, but to create a new semantic level in which one can
be absolutely precise.”
Edsger W. Dijkstra
– Nothing is ever hidden in software
– We can decompile JVM bytecode produced
via the Kotlin compiler
– In the future we could use IR
how do we find the truth?
by using a decompiler
Lies Told By The Kotlin Compiler
introducing javap
the java bytecode decompiler
% javap
Usage: javap <options> <classes>
where possible options include:
...
-p -private Show all classes and members
-c Disassemble the code
-s Print internal type signatures
...
--module-path <path> Specify where to find application modules
--system <jdk> Specify where to find system modules
...
-cp <path> Specify where to find user class files
...
– The Kotlin Language
– Suspending Functions & Coroutines
– Jetpack Compose (Desktop and Web)
Three different levels of deception
lies, damned lies and jetpack compose 
1
Lies from the
core language
Free Functions
//Program.kt
fun printMsg(text: String) = println(text)
fun main() = printMsg("Bonjour Kotlin Koders!")
free functions
Bonjour Kotlin Koders!
public final class ProgramKt {
public static final void printMsg(String);
Code:
15: return
public static final void main();
Code:
5: return
public static void main(String[]);
Code:
3: return
}
free functions
Nested Functions
fun main() {
fun printMsg(text: String) = println(text)
printMsg("Bonjour Kotlin Koders!")
}
nested functions
Bonjour Kotlin Koders!
public final class ProgramKt {
public static final void main();
Code:
0: ldc #8 // String Bonjour Kotlin Koders!
2: invokestatic #12 // Method main$printMsg:(String;)V
5: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #15 // Method main:()V
3: return
nested functions
private static final void main$printMsg(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: getstatic #23 // Field System.out
5: aload_0
6: invokevirtual #29 // Method PrintStream.println:(Object)V
9: return
}
nested functions
fun main() {
demo(123)
demo(45.6)
}
fun demo(input: Int) {
fun printMsg(text: String) = println(text)
printMsg("Hello Kotlin Koders!")
}
fun demo(input: Double) {
fun printMsg(text: String) = println(text)
printMsg("Bonjour Kotlin Koders!")
}
nested functions - extended
Hello Kotlin Koders!
Bonjour Kotlin Koders!
public static final void demo(int);
Code:
0: ldc #17 // String Hello Kotlin Koders!
2: invokestatic #21 // Method demo$printMsg:(String;)V
5: return
public static final void demo(double);
Code:
0: ldc #25 // String Bonjour Kotlin Koders!
2: invokestatic #28 // Method "demo$printMsg-0":(String;)V
5: return
nested functions - extended
private static final void demo$printMsg(String);
Code:
0: iconst_0
1: istore_1
2: getstatic #40 // Field System.out
5: aload_0
6: invokevirtual #46 // Method PrintStream.println:(Object;)V
9: return
private static final void demo$printMsg-0(String);
Code:
0: iconst_0
1: istore_1
2: getstatic #40 // Field System.out
5: aload_0
6: invokevirtual #46 // Method PrintStream.println:(Object;)V
9: return
}
nested functions - extended
Primary Constructors
class Person(val name: String, var age: Int) {
private val fullName: String
init {
fullName = "$name Jones"
}
override fun toString() = "$fullName aged $age"
}
fun main() {
val person = Person("Bridget", 30)
println(person)
}
constructors
Bridget Jones aged 30
public final class Person {
private final String name;
private int age;
private final String fullName;
public Person(String, int);
Code:
26: ldc #27 // String Jones
28: invokestatic #31 // Intrinsics.stringPlus:(String; Object;)String;
31: putfield #34 // Field fullName:String;
34: nop
35: return
public final String getName();
public final int getAge();
public final void setAge(int);
public String toString();
}
constructors
Extension Methods
class Person(val name: String)
fun Person.sayHello() = println("Hello from $name")
fun String.times(num: Int) = (1..num).joinToString { "$this" }
fun main() {
val person = Person("Jane")
person.sayHello()
println("Dave".times(3))
}
extension methods
Hello from Jane
Dave, Dave, Dave
public static final void sayHello(Person);
Code:
...
25: return
public static final String times(String, int);
Code:
...
40: return
extension methods
Destructuring
destructuring
data class Person(val name: String, val age: Int)
fun main() {
val person = Person("Lucy", 36)
val (x, y) = person
println(x)
println(y)
}
Lucy
36
destructuring
public final Person {
private final String name;
private final int age;
public Person(String, int);
public final String getName();
public final int getAge();
public final String component1();
public final int component2();
public final Person copy(String, int);
public static Person copy$default(Person, String, int, int, Object);
public java.lang.String toString();
public int hashCode();
public boolean equals(java.lang.Object);
}
destructuring
public final class ProgramKt {
public static final void main();
Code:
...
15: invokevirtual #18 // Person.component1:()String;
18: astore_2
19: aload_1
20: invokevirtual #22 // Person.component2:()I
23: istore_3
...
44: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #46 // Method main:()V
3: return
}
Object Declarations
object declarations
object Math {
fun add(no1: Int, no2: Int) = no1 + no2
}
fun main() {
println(Math.add(12,34))
}
46
object declarations
public final class Math {
public static final Math INSTANCE;
private Math();
public final int add(int, int);
static {};
Code:
0: new #2 // class Math
3: dup
4: invokespecial #17 // Method "<init>":()V
7: putstatic #20 // Field INSTANCE:Math;
10: return
}
object declarations
public final class ProgramKt {
public static final void main();
Code:
0: getstatic #12 // Field Math.INSTANCE:Math;
3: bipush 12
5: bipush 34
7: invokevirtual #16 // Method Math.add:(II)I
10: istore_0
11: iconst_0
12: istore_1
13: getstatic #22 // Field System.out;
16: iload_0
17: invokevirtual #28 // Method PrintStream.println:(I)V
20: return
public static void main(String[]);
}
Companion Objects
class Employee(val name: String, val dept: String) {
companion object {
fun buildForHR(name: String) = Employee(name, "HR")
fun buildForIT(name: String) = Employee(name, "IT")
}
override fun toString() = "$name working in $dept"
}
fun main() {
val emp1 = Employee.buildForHR("Dave")
val emp2 = Employee.buildForIT("Jane")
println(emp1)
println(emp2)
}
companion objects
Dave working in HR
Jane working in IT
public final class lang.eg8.Employee {
public static final lang.eg8.Employee$Companion Companion;
private final String name;
private final String dept;
public Employee(String, String);
public final String getName();
public final String getDept();
public String toString();
static {};
Code:
0: new #45 // class Employee$Companion
3: dup
4: aconst_null
5: invokespecial #48 // Employee$Companion."<init>"
8: putstatic #52 // Field Companion:Employee$Companion;
11: return
}
companion objects
Enumerations
enum class Colour {
Red,
Green,
Blue
}
fun main() {
Colour.values().forEach(::println)
}
Enumerations
Red
Green
Blue
public final class Colour extends java.lang.Enum<Colour> {
public static final Colour Red;
public static final Colour Green;
public static final Colour Blue;
private static final Colour[] $VALUES;
private Colour();
public static Colour[] values();
public static Colour valueOf(String);
private static final Colour[] $values();
Enumerations
static {};
Code:
0: new #2 // class Colour
3: dup
4: ldc #47 // String Red
6: iconst_0
7: invokespecial #48 // Method "<init>":(String;I)V
10: putstatic #39 // Field Red:Colour;
13: new #2 // class Colour
16: dup
17: ldc #49 // String Green
19: iconst_1
20: invokespecial #48 // Method "<init>":(String;I)V
23: putstatic #42 // Field Green:Colour;
Enumerations
26: new #2 // class Colour
30: ldc #50 // String Blue
32: iconst_2
33: invokespecial #48 // Method "<init>":(String;I)V
36: putstatic #45 // Field Blue:Colour;
39: invokestatic #52 // Method $values:()[Colour;
42: putstatic #22 // Field $VALUES:[Colour;
45: return
}
Enumerations
Lambdas With Receivers
data class Person(val name: String, var age: Int)
fun demo(person: Person, action: Person.() -> Unit) = person.apply(action)
fun main() {
val person = Person("Jane", 25)
demo(person) {
age += 10
println(this)
}
}
lambdas with receivers
Person(name=Jane, age=35)
public final class ProgramKt {
public static final Person demo(
Person,
kotlin.jvm.functions.Function1<? super Person, kotlin.Unit>
);
Code:
...
21: invokeinterface #24,2 //Function1.invoke:(Object;)Object;
...
public static final void main();
Code:
...
13: getstatic #42 // Field ProgramKt$main$1.INSTANCE:ProgramKt$main$1;
16: checkcast #20 // class kotlin/jvm/functions/Function1
19: invokestatic #44 // Method demo:(Person;Function1;)Person;
...
lambdas with receivers
public interface Function1<P1, R> extends Function<R> {
public abstract R invoke(P1);
}
lambdas with receivers
javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function1
public interface kotlin.Function<R> {
}
javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function
public interface Function2<P1, P2, R> extends Function<R> {
public abstract R invoke(P1, P2);
}
lambdas with receivers
javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function2
public interface Function3<P1, P2, P3, R> extends Function<R> {
public abstract R invoke(P1, P2, P3);
}
javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function3
2
Lies told when
suspending
– Do suspending functions actually suspend?
– Are they really paused and then resumed?
– Let’s use Ktor & Coroutines to investigate
– Because that’s the main use case
– Note other examples exist (e.g. Arrow)
consider suspending functions
is it all a lie?
– First, we write some entity classes
– To represent food and beverages
– Second, we write a server
– Which provides options for breakfast
– Finally, we write a client that suspends
– This is the code we will dissasemble
let’s order breakfast
via a REST endpoint
Data Model
enum class BeverageSize {
Small,
Medium,
Large
}
enum class BeverageType {
Expresso,
Americano,
CafeAuLait
}
@Serializable
class Beverage(val type: BeverageType, val size: BeverageSize) {
fun drink() = println("Drinking a $size $type")
}
our data model
@Serializable
class Food(
private val name: String,
private val servingSize: Int,
private val withChocolate: Boolean
) {
fun eat() {
val type = if(withChocolate) "chocolate" else "plain"
println("Eating $servingSize $type $name")
}
}
class Breakfast(private val beverage: Beverage, private val food: Food) {
fun consume() {
beverage.drink()
food.eat()
}
}
our data model
Server-Side
fun Application.configureBreakfast() {
routing {
get("/breakfast/beverage") {
call.respond(selectBeverage())
}
post("/breakfast/food") {
val beverage = call.receive<Beverage>()
call.respond(selectFood(beverage))
}
}
}
our server-side
fun selectBeverage(): Beverage {
fun inFrance() = Locale.getDefault() == FRANCE
fun beforeNoon() = LocalTime.now().isBefore(LocalTime.NOON)
val type = if (inFrance()) CafeAuLait else Americano
val size = if (beforeNoon()) Large else Medium
return Beverage(type, size)
}
fun selectFood(beverage: Beverage): Food {
val serving = if (beverage.size == Large) 3 else 2
return if (beverage.type == CafeAuLait) {
Food("Croissants", serving, true)
} else {
Food("Pancakes", serving, false)
}
}
our server-side
Client-Side
fun main() = runBlocking {
printInDetail("Program starts")
val client = buildHttpClient()
client.use {
val breakfast = orderBreakfast(client)
printInDetail("Breakfast ordered")
breakfast.consume()
}
printInDetail("Program ends")
}
our client
[Program starts at 12:21:16 on 1]
[Ordering breakfast at 12:21:16 on 24]
[Breakfast ordered at 12:21:17 on 1]
Drinking a Medium Americano
Eating 2 plain Pancakes
[Program ends at 12:21:17 on 1]
fun printInDetail(item: String) {
fun timeNow(): String {
val formatter = DateTimeFormatter.ofPattern("HH:mm:ss")
return formatter.format(LocalTime.now())
}
fun timeAndThread(item: String) =
"[$item at ${timeNow()} on ${Thread.currentThread().id}]"
println(timeAndThread(item))
}
our client
private fun buildHttpClient() = HttpClient(CIO) {
install(JsonFeature)
defaultRequest {
host = "0.0.0.0"
port = 8080
url {
protocol = URLProtocol.HTTP
}
}
}
our client
suspend fun orderBreakfast(
client: HttpClient
): Breakfast = withContext(Dispatchers.IO) {
printInDetail("Ordering breakfast")
val beverage: Beverage = orderBeverage(client)
val food: Food = orderFood(client, beverage)
Breakfast(beverage, food)
}
our client
private suspend fun orderFood(it: HttpClient, beverage: Beverage): Food =
it.post("/breakfast/food") {
body = beverage
contentType(ContentType.Application.Json)
}
private suspend fun orderBeverage(it: HttpClient): Beverage =
it.get("/breakfast/beverage") {
accept(ContentType.Application.Json)
}
our client
– Note this is a suspending function
– Which calls two other suspending functions
let’s disassemble ‘orderBreakfast’
to see what lies are found within...
public static final Object orderBreakfast(...) {
return BuildersKt.withContext(... , ... {
...
public final Object invokeSuspend(...) {
...
label17: {
...
switch(this.label) {
case 0:
...
case 1:
...
case 2:
...
default:
...
}
...
}
...
}
...
}), ...);
}
let’s disassemble ‘orderBreakfast’
why it’s just a big switch statement!
...nested inside a labelled block
understanding suspending functions
label17: {
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
HttpClient var5;
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
UtilsKt.printInDetail("Ordering breakfast");
var5 = client;
this.label = 1;
var10000 = ProgramKt.orderBeverage(var5, this);
if (var10000 == var4) {
return var4;
}
break;
var4 will hold the
constant value for
signaling suspension
we suspend by returning if that’s what
the orderBeverage function wants
how do suspending functions work?
via continuations...
Lies Told By The Kotlin Compiler
understanding suspending functions
beverage = (Beverage)var10000;
var5 = client;
this.L$0 = beverage;
this.label = 2;
var10000 = ProgramKt.orderFood(var5, beverage, this);
if (var10000 == var4) {
return var4;
}
when we have the return value
from orderBeverage we store it
and set the label to 2
we suspend by returning if that’s
what the orderFood function wants
understanding suspending functions
case 2:
beverage = (Beverage)this.L$0;
ResultKt.throwOnFailure($result);
var10000 = $result;
break label17;
when we have the return value from
orderFood we retrieve the beverage from
storage, put the food in var10000 and use a
labelled block to exit the switch statement
understanding suspending functions
public final Object invokeSuspend(@NotNull Object $result) {
Object var10000;
Beverage beverage;
label17: {
//switch statement lives here
}
Food food = (Food)var10000;
return new Breakfast(beverage, food);
}
Once the mechanics of calling the
other suspending functions are
complete, we can build the final return
value from the incremental results
– Continuation objects hold labels and
intermediate results
– A switch statement resumes
processing at the correct point
– A labelled block short-circuits the
switch when we are done
suspending functions do not suspend!!!
they return and are re-invoked
3
Lies told by
compose
Lies Told By The Kotlin Compiler
Lies Told By The Kotlin Compiler
working with desktop compose
creating a simple calculator
– Compose is implemented as a DSL
– But goes beyond a purely Internal DSL
– A compiler plugin is required
– To generate supporting infrastructure
– This implies we are being deceived
– Let’s look at a sample application...
fun main() = application {
Window(...) {
val savedTotal = remember { mutableStateOf(0) }
val displayedTotal = remember { mutableStateOf(0) }
val operationOngoing = remember { mutableStateOf(Operation.None) }
val operationJustChanged = remember { mutableStateOf(false) }
//Event Handlers Omitted
InstilTheme {
Column {
Row {
DisplayText(text = "${displayedTotal.value}")
}
Row {
NumberButtonColumn(numberSelected)
OperationButtonColumn(operationSelected, clearSelected, equalsSelected)
}
}
}
}
}
building a calculator via compose
@Composable
fun InstilTheme(content: @Composable () -> Unit) {
val colors = MaterialTheme.colors.copy(primary = Color.LightGray)
MaterialTheme(content = content, colors = colors)
}
@Composable
fun DisplayText(text: String) =
Text(
text = text,
style = TextStyle(color = Color.Black, fontSize = 28.sp),
modifier = Modifier.padding(all = 10.dp)
)
building a calculator via compose
@Composable
fun NumberButton(onClick: () -> Unit, number: Int) =
Button(
onClick = onClick,
modifier = Modifier.padding(all = 5.dp)
) {
Text(
number.toString(),
style = TextStyle(color = Color.Black, fontSize = 18.sp)
)
}
@Composable
fun NumberButtonRow(range: IntRange, onClick: (Int) -> Unit) = Row {
range.forEach { num ->
NumberButton(onClick = { onClick(num) }, number = num)
}
}
building a calculator via compose
@Composable
fun NumberButtonColumn(onClick: (Int) -> Unit) = Column {
NumberButtonRow(1..3, onClick)
NumberButtonRow(4..6, onClick)
NumberButtonRow(7..9, onClick)
NumberButtonRow(0..0, onClick)
}
building a calculator via compose
@Composable
fun OperationButton(onClick: () -> Unit, label: String) =
Button(
onClick = onClick,
modifier = Modifier.padding(all = 2.dp)
) {
Text(
label,
style = TextStyle(color = Color.Black, fontSize = 14.sp)
)
}
building a calculator via compose
@Composable
fun OperationButtonColumn(
onSelect: (Operation) -> Unit,
onClear: () -> Unit,
onEquals: () -> Unit
) = Column {
listOf(
{ onSelect(Add) } to "+",
{ onSelect(Subtract) } to "-",
{ onSelect(Multiply) } to "*",
{ onSelect(Divide) } to "/",
onClear to "Clear",
onEquals to "="
).forEach {
OperationButton(onClick = it.first, label = it.second)
}
}
building a calculator via compose
@Composable
fun DisplayText(text: String) =
Text(
text = text,
style = TextStyle(color = Color.Black, fontSize = 28.sp),
modifier = Modifier.padding(all = 10.dp)
)
public static final void DisplayText(String, Composer, int);
Code:
0: aload_0
// Lots and lots of bytecode...
791: return
decompiling the calculator
– Every compose functions takes:
– An object of type composer
– A unique integer value
– The composer builds the logical tree of widgets
– The integer uniquely identifies an instance of a widget
decompiling the calculator
the additional parameters
public final class CalculatorKt {
public static final void InstilTheme(
Function2<? super Composer, ? super Integer, Unit>,
Composer,
int
);
public static final void DisplayText(
String,
Composer,
int
);
public static final void NumberButton(
Function0<Unit>,
int,
Composer,
int
);
decompiling the calculator
public static final void OperationButton(
Function0<Unit>,
String,
Composer,
int
);
public static final void OperationButtonColumn(
Function1<? super Operation, Unit>,
Function0<Unit>, Function0<Unit>,
Composer,
int
);
decompiling the calculator
public static final void NumberButtonRow(
IntRange,
Function1<? super Integer, Unit>,
Composer,
int
);
public static final void NumberButtonColumn(
Function1<? super Integer, Unit>,
Composer,
int
);
public static final void main();
public static void main(String[]);
}
decompiling the calculator
– Composable functions support Positional Memoization
– Memory of how they were called at each point on the graph
– They can also be re-invoked at any time (Recomposition)
– This is accomplished via a Gap Buffer / Slot Table
decompiling the calculator
groups and the slot table
– On every call we insert a new group into the table
– The group is identified by the integer parameter
– Nested components add groups for child components
– We walk the graph depth first to build a linear structure
decompiling the calculator
groups and the slot table
Group
(123)
Group
(456)
Group
(789)
public static final void DisplayText(String, Composer, int);
Code:
7: ldc #110 // int 1244737340
9: invokeinterface #25, 2 // Composer.startRestartGroup:(I)
...
33: invokeinterface #37, 2 // Composer.changed:(Object)
...
58: invokeinterface #41, 1 // Composer.getSkipping:()
...
167: invokeinterface #79, 1 // Composer.skipToGroupEnd:()V
...
173: invokeinterface #83, 1 // Composer.endRestartGroup:()
...
202: invokeinterface #97, 2 // ScopeUpdateScope.updateScope:(Function2)
207: return
decompiling the calculator
Lies Told By The Kotlin Compiler
4
Conclusions
– They provide advanced features
– They provide consistency across all the
platforms the language supports
– They let us work at higher levels of abstraction
(with certainty)
– They get us home by 5pm 
the lies are everywhere!
...and that’s a good thing
Questions?

More Related Content

PPTX
Lies Told By The Kotlin Compiler
PPTX
Kotlin / Android Update
PPTX
Kotlin decompiled
PDF
Kotlin Bytecode Generation and Runtime Performance
PDF
Intro to Kotlin
PDF
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016
PDF
Kotlin Developer Starter in Android projects
PPTX
KotlinForJavaDevelopers-UJUG.pptx
Lies Told By The Kotlin Compiler
Kotlin / Android Update
Kotlin decompiled
Kotlin Bytecode Generation and Runtime Performance
Intro to Kotlin
Kotlin Developer Starter in Android - STX Next Lightning Talks - Feb 12, 2016
Kotlin Developer Starter in Android projects
KotlinForJavaDevelopers-UJUG.pptx

Similar to Lies Told By The Kotlin Compiler (20)

PDF
Kotlin for Android Developers - 3
PDF
Kotlin/Everywhere GDG Bhubaneswar 2019
PPTX
Benefits of Kotlin
PDF
ADG Poznań - Kotlin for Android developers
PDF
Kotlin cheat sheet by ekito
PDF
2 kotlin vs. java: what java has that kotlin does not
PPT
The Kotlin Programming Language
PPTX
Introduction to kotlin + spring boot demo
PPTX
Kotlin Basic & Android Programming
PDF
Privet Kotlin (Windy City DevFest)
PDF
Kotlin @ Devoxx 2011
PDF
Kotlin Slides from Devoxx 2011
PDF
Kotlin: A pragmatic language by JetBrains
PDF
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
PDF
Bologna Developer Zone - About Kotlin
PDF
Meetup di GDG Italia - Leonardo Pirro - Codemotion Rome 2018
PPTX
Kotlin Language Features - A Java comparison
PPTX
Building Mobile Apps with Android
PDF
Kotlin intro
Kotlin for Android Developers - 3
Kotlin/Everywhere GDG Bhubaneswar 2019
Benefits of Kotlin
ADG Poznań - Kotlin for Android developers
Kotlin cheat sheet by ekito
2 kotlin vs. java: what java has that kotlin does not
The Kotlin Programming Language
Introduction to kotlin + spring boot demo
Kotlin Basic & Android Programming
Privet Kotlin (Windy City DevFest)
Kotlin @ Devoxx 2011
Kotlin Slides from Devoxx 2011
Kotlin: A pragmatic language by JetBrains
Kotlin - The Swiss army knife of programming languages - Visma Mobile Meet-up...
Bologna Developer Zone - About Kotlin
Meetup di GDG Italia - Leonardo Pirro - Codemotion Rome 2018
Kotlin Language Features - A Java comparison
Building Mobile Apps with Android
Kotlin intro

More from Garth Gilmour (20)

PPTX
Compose in Theory
PPTX
TypeScript Vs. KotlinJS
PPTX
Shut Up And Eat Your Veg
PPTX
A TypeScript Fans KotlinJS Adventures
PPTX
The Heat Death Of Enterprise IT
PPTX
Type Driven Development with TypeScript
PPTX
Generics On The JVM (What you don't know will hurt you)
PPTX
Using Kotlin, to Create Kotlin, to Teach Kotlin, in Space
PPTX
Is Software Engineering A Profession?
PPTX
Social Distancing is not Behaving Distantly
PDF
The Great Scala Makeover
PDF
Transitioning Android Teams Into Kotlin
PDF
Simpler and Safer Java Types (via the Vavr and Lambda Libraries)
PDF
The Three Horse Race
PDF
The Bestiary of Pure Functional Programming
PDF
BelTech 2019 Presenters Workshop
PDF
Kotlin The Whole Damn Family
PDF
The Philosophy of DDD
PDF
10 Big Ideas from Industry
PDF
'Full Stack Kotlin' Workshop at KotlinConf
Compose in Theory
TypeScript Vs. KotlinJS
Shut Up And Eat Your Veg
A TypeScript Fans KotlinJS Adventures
The Heat Death Of Enterprise IT
Type Driven Development with TypeScript
Generics On The JVM (What you don't know will hurt you)
Using Kotlin, to Create Kotlin, to Teach Kotlin, in Space
Is Software Engineering A Profession?
Social Distancing is not Behaving Distantly
The Great Scala Makeover
Transitioning Android Teams Into Kotlin
Simpler and Safer Java Types (via the Vavr and Lambda Libraries)
The Three Horse Race
The Bestiary of Pure Functional Programming
BelTech 2019 Presenters Workshop
Kotlin The Whole Damn Family
The Philosophy of DDD
10 Big Ideas from Industry
'Full Stack Kotlin' Workshop at KotlinConf

Recently uploaded (20)

PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
Softaken Excel to vCard Converter Software.pdf
PPTX
Odoo POS Development Services by CandidRoot Solutions
PDF
Understanding Forklifts - TECH EHS Solution
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PDF
System and Network Administraation Chapter 3
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PPTX
CHAPTER 2 - PM Management and IT Context
PPTX
Introduction to Artificial Intelligence
PPTX
history of c programming in notes for students .pptx
PDF
Digital Strategies for Manufacturing Companies
PDF
top salesforce developer skills in 2025.pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
Design an Analysis of Algorithms II-SECS-1021-03
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Softaken Excel to vCard Converter Software.pdf
Odoo POS Development Services by CandidRoot Solutions
Understanding Forklifts - TECH EHS Solution
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Upgrade and Innovation Strategies for SAP ERP Customers
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
System and Network Administraation Chapter 3
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
CHAPTER 2 - PM Management and IT Context
Introduction to Artificial Intelligence
history of c programming in notes for students .pptx
Digital Strategies for Manufacturing Companies
top salesforce developer skills in 2025.pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
VVF-Customer-Presentation2025-Ver1.9.pptx

Lies Told By The Kotlin Compiler

  • 1. © Instil Software 2020 Lies Told By The Kotlin Compiler Looking under the hood of Kotlin JVM
  • 4. – Kotlin is an awesome language! – It provides many wonderful features – These features don’t exist on the supported platforms – We need the compiler to emulate them – Initially we don’t care how this is done introducing this talk why do we need lies?
  • 5. the lies are a good thing they create a more productive world "Being abstract is something profoundly different from being vague … The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.” Edsger W. Dijkstra
  • 6. – Nothing is ever hidden in software – We can decompile JVM bytecode produced via the Kotlin compiler – In the future we could use IR how do we find the truth? by using a decompiler
  • 8. introducing javap the java bytecode decompiler % javap Usage: javap <options> <classes> where possible options include: ... -p -private Show all classes and members -c Disassemble the code -s Print internal type signatures ... --module-path <path> Specify where to find application modules --system <jdk> Specify where to find system modules ... -cp <path> Specify where to find user class files ...
  • 9. – The Kotlin Language – Suspending Functions & Coroutines – Jetpack Compose (Desktop and Web) Three different levels of deception lies, damned lies and jetpack compose 
  • 12. //Program.kt fun printMsg(text: String) = println(text) fun main() = printMsg("Bonjour Kotlin Koders!") free functions Bonjour Kotlin Koders!
  • 13. public final class ProgramKt { public static final void printMsg(String); Code: 15: return public static final void main(); Code: 5: return public static void main(String[]); Code: 3: return } free functions
  • 15. fun main() { fun printMsg(text: String) = println(text) printMsg("Bonjour Kotlin Koders!") } nested functions Bonjour Kotlin Koders!
  • 16. public final class ProgramKt { public static final void main(); Code: 0: ldc #8 // String Bonjour Kotlin Koders! 2: invokestatic #12 // Method main$printMsg:(String;)V 5: return public static void main(java.lang.String[]); Code: 0: invokestatic #15 // Method main:()V 3: return nested functions
  • 17. private static final void main$printMsg(java.lang.String); Code: 0: iconst_0 1: istore_1 2: getstatic #23 // Field System.out 5: aload_0 6: invokevirtual #29 // Method PrintStream.println:(Object)V 9: return } nested functions
  • 18. fun main() { demo(123) demo(45.6) } fun demo(input: Int) { fun printMsg(text: String) = println(text) printMsg("Hello Kotlin Koders!") } fun demo(input: Double) { fun printMsg(text: String) = println(text) printMsg("Bonjour Kotlin Koders!") } nested functions - extended Hello Kotlin Koders! Bonjour Kotlin Koders!
  • 19. public static final void demo(int); Code: 0: ldc #17 // String Hello Kotlin Koders! 2: invokestatic #21 // Method demo$printMsg:(String;)V 5: return public static final void demo(double); Code: 0: ldc #25 // String Bonjour Kotlin Koders! 2: invokestatic #28 // Method "demo$printMsg-0":(String;)V 5: return nested functions - extended
  • 20. private static final void demo$printMsg(String); Code: 0: iconst_0 1: istore_1 2: getstatic #40 // Field System.out 5: aload_0 6: invokevirtual #46 // Method PrintStream.println:(Object;)V 9: return private static final void demo$printMsg-0(String); Code: 0: iconst_0 1: istore_1 2: getstatic #40 // Field System.out 5: aload_0 6: invokevirtual #46 // Method PrintStream.println:(Object;)V 9: return } nested functions - extended
  • 22. class Person(val name: String, var age: Int) { private val fullName: String init { fullName = "$name Jones" } override fun toString() = "$fullName aged $age" } fun main() { val person = Person("Bridget", 30) println(person) } constructors Bridget Jones aged 30
  • 23. public final class Person { private final String name; private int age; private final String fullName; public Person(String, int); Code: 26: ldc #27 // String Jones 28: invokestatic #31 // Intrinsics.stringPlus:(String; Object;)String; 31: putfield #34 // Field fullName:String; 34: nop 35: return public final String getName(); public final int getAge(); public final void setAge(int); public String toString(); } constructors
  • 25. class Person(val name: String) fun Person.sayHello() = println("Hello from $name") fun String.times(num: Int) = (1..num).joinToString { "$this" } fun main() { val person = Person("Jane") person.sayHello() println("Dave".times(3)) } extension methods Hello from Jane Dave, Dave, Dave
  • 26. public static final void sayHello(Person); Code: ... 25: return public static final String times(String, int); Code: ... 40: return extension methods
  • 28. destructuring data class Person(val name: String, val age: Int) fun main() { val person = Person("Lucy", 36) val (x, y) = person println(x) println(y) } Lucy 36
  • 29. destructuring public final Person { private final String name; private final int age; public Person(String, int); public final String getName(); public final int getAge(); public final String component1(); public final int component2(); public final Person copy(String, int); public static Person copy$default(Person, String, int, int, Object); public java.lang.String toString(); public int hashCode(); public boolean equals(java.lang.Object); }
  • 30. destructuring public final class ProgramKt { public static final void main(); Code: ... 15: invokevirtual #18 // Person.component1:()String; 18: astore_2 19: aload_1 20: invokevirtual #22 // Person.component2:()I 23: istore_3 ... 44: return public static void main(java.lang.String[]); Code: 0: invokestatic #46 // Method main:()V 3: return }
  • 32. object declarations object Math { fun add(no1: Int, no2: Int) = no1 + no2 } fun main() { println(Math.add(12,34)) } 46
  • 33. object declarations public final class Math { public static final Math INSTANCE; private Math(); public final int add(int, int); static {}; Code: 0: new #2 // class Math 3: dup 4: invokespecial #17 // Method "<init>":()V 7: putstatic #20 // Field INSTANCE:Math; 10: return }
  • 34. object declarations public final class ProgramKt { public static final void main(); Code: 0: getstatic #12 // Field Math.INSTANCE:Math; 3: bipush 12 5: bipush 34 7: invokevirtual #16 // Method Math.add:(II)I 10: istore_0 11: iconst_0 12: istore_1 13: getstatic #22 // Field System.out; 16: iload_0 17: invokevirtual #28 // Method PrintStream.println:(I)V 20: return public static void main(String[]); }
  • 36. class Employee(val name: String, val dept: String) { companion object { fun buildForHR(name: String) = Employee(name, "HR") fun buildForIT(name: String) = Employee(name, "IT") } override fun toString() = "$name working in $dept" } fun main() { val emp1 = Employee.buildForHR("Dave") val emp2 = Employee.buildForIT("Jane") println(emp1) println(emp2) } companion objects Dave working in HR Jane working in IT
  • 37. public final class lang.eg8.Employee { public static final lang.eg8.Employee$Companion Companion; private final String name; private final String dept; public Employee(String, String); public final String getName(); public final String getDept(); public String toString(); static {}; Code: 0: new #45 // class Employee$Companion 3: dup 4: aconst_null 5: invokespecial #48 // Employee$Companion."<init>" 8: putstatic #52 // Field Companion:Employee$Companion; 11: return } companion objects
  • 39. enum class Colour { Red, Green, Blue } fun main() { Colour.values().forEach(::println) } Enumerations Red Green Blue
  • 40. public final class Colour extends java.lang.Enum<Colour> { public static final Colour Red; public static final Colour Green; public static final Colour Blue; private static final Colour[] $VALUES; private Colour(); public static Colour[] values(); public static Colour valueOf(String); private static final Colour[] $values(); Enumerations
  • 41. static {}; Code: 0: new #2 // class Colour 3: dup 4: ldc #47 // String Red 6: iconst_0 7: invokespecial #48 // Method "<init>":(String;I)V 10: putstatic #39 // Field Red:Colour; 13: new #2 // class Colour 16: dup 17: ldc #49 // String Green 19: iconst_1 20: invokespecial #48 // Method "<init>":(String;I)V 23: putstatic #42 // Field Green:Colour; Enumerations
  • 42. 26: new #2 // class Colour 30: ldc #50 // String Blue 32: iconst_2 33: invokespecial #48 // Method "<init>":(String;I)V 36: putstatic #45 // Field Blue:Colour; 39: invokestatic #52 // Method $values:()[Colour; 42: putstatic #22 // Field $VALUES:[Colour; 45: return } Enumerations
  • 44. data class Person(val name: String, var age: Int) fun demo(person: Person, action: Person.() -> Unit) = person.apply(action) fun main() { val person = Person("Jane", 25) demo(person) { age += 10 println(this) } } lambdas with receivers Person(name=Jane, age=35)
  • 45. public final class ProgramKt { public static final Person demo( Person, kotlin.jvm.functions.Function1<? super Person, kotlin.Unit> ); Code: ... 21: invokeinterface #24,2 //Function1.invoke:(Object;)Object; ... public static final void main(); Code: ... 13: getstatic #42 // Field ProgramKt$main$1.INSTANCE:ProgramKt$main$1; 16: checkcast #20 // class kotlin/jvm/functions/Function1 19: invokestatic #44 // Method demo:(Person;Function1;)Person; ... lambdas with receivers
  • 46. public interface Function1<P1, R> extends Function<R> { public abstract R invoke(P1); } lambdas with receivers javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function1 public interface kotlin.Function<R> { } javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function
  • 47. public interface Function2<P1, P2, R> extends Function<R> { public abstract R invoke(P1, P2); } lambdas with receivers javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function2 public interface Function3<P1, P2, P3, R> extends Function<R> { public abstract R invoke(P1, P2, P3); } javap -cp kotlin-stdlib-1.5.10.jar -c -p kotlin.jvm.functions.Function3
  • 49. – Do suspending functions actually suspend? – Are they really paused and then resumed? – Let’s use Ktor & Coroutines to investigate – Because that’s the main use case – Note other examples exist (e.g. Arrow) consider suspending functions is it all a lie?
  • 50. – First, we write some entity classes – To represent food and beverages – Second, we write a server – Which provides options for breakfast – Finally, we write a client that suspends – This is the code we will dissasemble let’s order breakfast via a REST endpoint
  • 52. enum class BeverageSize { Small, Medium, Large } enum class BeverageType { Expresso, Americano, CafeAuLait } @Serializable class Beverage(val type: BeverageType, val size: BeverageSize) { fun drink() = println("Drinking a $size $type") } our data model
  • 53. @Serializable class Food( private val name: String, private val servingSize: Int, private val withChocolate: Boolean ) { fun eat() { val type = if(withChocolate) "chocolate" else "plain" println("Eating $servingSize $type $name") } } class Breakfast(private val beverage: Beverage, private val food: Food) { fun consume() { beverage.drink() food.eat() } } our data model
  • 55. fun Application.configureBreakfast() { routing { get("/breakfast/beverage") { call.respond(selectBeverage()) } post("/breakfast/food") { val beverage = call.receive<Beverage>() call.respond(selectFood(beverage)) } } } our server-side
  • 56. fun selectBeverage(): Beverage { fun inFrance() = Locale.getDefault() == FRANCE fun beforeNoon() = LocalTime.now().isBefore(LocalTime.NOON) val type = if (inFrance()) CafeAuLait else Americano val size = if (beforeNoon()) Large else Medium return Beverage(type, size) } fun selectFood(beverage: Beverage): Food { val serving = if (beverage.size == Large) 3 else 2 return if (beverage.type == CafeAuLait) { Food("Croissants", serving, true) } else { Food("Pancakes", serving, false) } } our server-side
  • 58. fun main() = runBlocking { printInDetail("Program starts") val client = buildHttpClient() client.use { val breakfast = orderBreakfast(client) printInDetail("Breakfast ordered") breakfast.consume() } printInDetail("Program ends") } our client [Program starts at 12:21:16 on 1] [Ordering breakfast at 12:21:16 on 24] [Breakfast ordered at 12:21:17 on 1] Drinking a Medium Americano Eating 2 plain Pancakes [Program ends at 12:21:17 on 1]
  • 59. fun printInDetail(item: String) { fun timeNow(): String { val formatter = DateTimeFormatter.ofPattern("HH:mm:ss") return formatter.format(LocalTime.now()) } fun timeAndThread(item: String) = "[$item at ${timeNow()} on ${Thread.currentThread().id}]" println(timeAndThread(item)) } our client
  • 60. private fun buildHttpClient() = HttpClient(CIO) { install(JsonFeature) defaultRequest { host = "0.0.0.0" port = 8080 url { protocol = URLProtocol.HTTP } } } our client
  • 61. suspend fun orderBreakfast( client: HttpClient ): Breakfast = withContext(Dispatchers.IO) { printInDetail("Ordering breakfast") val beverage: Beverage = orderBeverage(client) val food: Food = orderFood(client, beverage) Breakfast(beverage, food) } our client
  • 62. private suspend fun orderFood(it: HttpClient, beverage: Beverage): Food = it.post("/breakfast/food") { body = beverage contentType(ContentType.Application.Json) } private suspend fun orderBeverage(it: HttpClient): Beverage = it.get("/breakfast/beverage") { accept(ContentType.Application.Json) } our client
  • 63. – Note this is a suspending function – Which calls two other suspending functions let’s disassemble ‘orderBreakfast’ to see what lies are found within...
  • 64. public static final Object orderBreakfast(...) { return BuildersKt.withContext(... , ... { ... public final Object invokeSuspend(...) { ... label17: { ... switch(this.label) { case 0: ... case 1: ... case 2: ... default: ... } ... } ... } ... }), ...); } let’s disassemble ‘orderBreakfast’
  • 65. why it’s just a big switch statement! ...nested inside a labelled block
  • 66. understanding suspending functions label17: { Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); HttpClient var5; switch(this.label) { case 0: ResultKt.throwOnFailure($result); UtilsKt.printInDetail("Ordering breakfast"); var5 = client; this.label = 1; var10000 = ProgramKt.orderBeverage(var5, this); if (var10000 == var4) { return var4; } break; var4 will hold the constant value for signaling suspension we suspend by returning if that’s what the orderBeverage function wants
  • 67. how do suspending functions work? via continuations...
  • 69. understanding suspending functions beverage = (Beverage)var10000; var5 = client; this.L$0 = beverage; this.label = 2; var10000 = ProgramKt.orderFood(var5, beverage, this); if (var10000 == var4) { return var4; } when we have the return value from orderBeverage we store it and set the label to 2 we suspend by returning if that’s what the orderFood function wants
  • 70. understanding suspending functions case 2: beverage = (Beverage)this.L$0; ResultKt.throwOnFailure($result); var10000 = $result; break label17; when we have the return value from orderFood we retrieve the beverage from storage, put the food in var10000 and use a labelled block to exit the switch statement
  • 71. understanding suspending functions public final Object invokeSuspend(@NotNull Object $result) { Object var10000; Beverage beverage; label17: { //switch statement lives here } Food food = (Food)var10000; return new Breakfast(beverage, food); } Once the mechanics of calling the other suspending functions are complete, we can build the final return value from the incremental results
  • 72. – Continuation objects hold labels and intermediate results – A switch statement resumes processing at the correct point – A labelled block short-circuits the switch when we are done suspending functions do not suspend!!! they return and are re-invoked
  • 76. working with desktop compose creating a simple calculator – Compose is implemented as a DSL – But goes beyond a purely Internal DSL – A compiler plugin is required – To generate supporting infrastructure – This implies we are being deceived – Let’s look at a sample application...
  • 77. fun main() = application { Window(...) { val savedTotal = remember { mutableStateOf(0) } val displayedTotal = remember { mutableStateOf(0) } val operationOngoing = remember { mutableStateOf(Operation.None) } val operationJustChanged = remember { mutableStateOf(false) } //Event Handlers Omitted InstilTheme { Column { Row { DisplayText(text = "${displayedTotal.value}") } Row { NumberButtonColumn(numberSelected) OperationButtonColumn(operationSelected, clearSelected, equalsSelected) } } } } } building a calculator via compose
  • 78. @Composable fun InstilTheme(content: @Composable () -> Unit) { val colors = MaterialTheme.colors.copy(primary = Color.LightGray) MaterialTheme(content = content, colors = colors) } @Composable fun DisplayText(text: String) = Text( text = text, style = TextStyle(color = Color.Black, fontSize = 28.sp), modifier = Modifier.padding(all = 10.dp) ) building a calculator via compose
  • 79. @Composable fun NumberButton(onClick: () -> Unit, number: Int) = Button( onClick = onClick, modifier = Modifier.padding(all = 5.dp) ) { Text( number.toString(), style = TextStyle(color = Color.Black, fontSize = 18.sp) ) } @Composable fun NumberButtonRow(range: IntRange, onClick: (Int) -> Unit) = Row { range.forEach { num -> NumberButton(onClick = { onClick(num) }, number = num) } } building a calculator via compose
  • 80. @Composable fun NumberButtonColumn(onClick: (Int) -> Unit) = Column { NumberButtonRow(1..3, onClick) NumberButtonRow(4..6, onClick) NumberButtonRow(7..9, onClick) NumberButtonRow(0..0, onClick) } building a calculator via compose
  • 81. @Composable fun OperationButton(onClick: () -> Unit, label: String) = Button( onClick = onClick, modifier = Modifier.padding(all = 2.dp) ) { Text( label, style = TextStyle(color = Color.Black, fontSize = 14.sp) ) } building a calculator via compose
  • 82. @Composable fun OperationButtonColumn( onSelect: (Operation) -> Unit, onClear: () -> Unit, onEquals: () -> Unit ) = Column { listOf( { onSelect(Add) } to "+", { onSelect(Subtract) } to "-", { onSelect(Multiply) } to "*", { onSelect(Divide) } to "/", onClear to "Clear", onEquals to "=" ).forEach { OperationButton(onClick = it.first, label = it.second) } } building a calculator via compose
  • 83. @Composable fun DisplayText(text: String) = Text( text = text, style = TextStyle(color = Color.Black, fontSize = 28.sp), modifier = Modifier.padding(all = 10.dp) ) public static final void DisplayText(String, Composer, int); Code: 0: aload_0 // Lots and lots of bytecode... 791: return decompiling the calculator
  • 84. – Every compose functions takes: – An object of type composer – A unique integer value – The composer builds the logical tree of widgets – The integer uniquely identifies an instance of a widget decompiling the calculator the additional parameters
  • 85. public final class CalculatorKt { public static final void InstilTheme( Function2<? super Composer, ? super Integer, Unit>, Composer, int ); public static final void DisplayText( String, Composer, int ); public static final void NumberButton( Function0<Unit>, int, Composer, int ); decompiling the calculator
  • 86. public static final void OperationButton( Function0<Unit>, String, Composer, int ); public static final void OperationButtonColumn( Function1<? super Operation, Unit>, Function0<Unit>, Function0<Unit>, Composer, int ); decompiling the calculator
  • 87. public static final void NumberButtonRow( IntRange, Function1<? super Integer, Unit>, Composer, int ); public static final void NumberButtonColumn( Function1<? super Integer, Unit>, Composer, int ); public static final void main(); public static void main(String[]); } decompiling the calculator
  • 88. – Composable functions support Positional Memoization – Memory of how they were called at each point on the graph – They can also be re-invoked at any time (Recomposition) – This is accomplished via a Gap Buffer / Slot Table decompiling the calculator groups and the slot table
  • 89. – On every call we insert a new group into the table – The group is identified by the integer parameter – Nested components add groups for child components – We walk the graph depth first to build a linear structure decompiling the calculator groups and the slot table Group (123) Group (456) Group (789)
  • 90. public static final void DisplayText(String, Composer, int); Code: 7: ldc #110 // int 1244737340 9: invokeinterface #25, 2 // Composer.startRestartGroup:(I) ... 33: invokeinterface #37, 2 // Composer.changed:(Object) ... 58: invokeinterface #41, 1 // Composer.getSkipping:() ... 167: invokeinterface #79, 1 // Composer.skipToGroupEnd:()V ... 173: invokeinterface #83, 1 // Composer.endRestartGroup:() ... 202: invokeinterface #97, 2 // ScopeUpdateScope.updateScope:(Function2) 207: return decompiling the calculator
  • 93. – They provide advanced features – They provide consistency across all the platforms the language supports – They let us work at higher levels of abstraction (with certainty) – They get us home by 5pm  the lies are everywhere! ...and that’s a good thing