Slide 1

Slide 1 text

Kotlin Compiler Plugin 맛보기 2024. 11. 09. 성빈 미니 행사

Slide 2

Slide 2 text

후원해 주신 김성윤, 김택우 님 감사합니다! 성윤 님의 E스포츠 코칭 서비스: is.gd/WinTracker 택우 님이 기여하신 디스코드 봇 프레임워크: github.com/harmonyland/harmony

Slide 3

Slide 3 text

Ji Sungbin [email protected] github.com/jisungbin sungb.in

Slide 4

Slide 4 text

우리가 무엇을 맛보게 될까?

Slide 5

Slide 5 text

코틀린 컴파일 과정에 직접 관여할 수 있는 강력한 도구 Kotlin Compiler Plugin

Slide 6

Slide 6 text

코드 형태 분석 우리가 작성한 코드를 섬세한 토큰 단위로 분석하여, 이 API가 올바르게 사용되었는지, 코틀린 문법상 문제는 없는지, 최적화 가능한 요소가 있는지 등, 언어의 표면적인 부분을 검사합니다. 동적 코드 변형 분석된 코드 형태를 보고 다양한 최적화를 적용하거나, 기존에는 없던 새로운 코드를
 추가합니다. 새로운 키워드 추가 (X) 아쉽게도 못하는 기능 Kotlin Compiler Plugin

Slide 7

Slide 7 text

코드 형태 분석 우리가 작성한 코드를 섬세한 토큰 단위로 분석하여, 이 API가 올바르게 사용되었는지, 코틀린 문법상 문제는 없는지, 최적화 가능한 요소가 있는지 등, 언어의 표면적인 부분을 검사합니다. 동적 코드 변형 분석된 코드 형태를 보고 다양한 최적화를 적용하거나, 기존에는 없던 새로운 코드를
 추가합니다. 새로운 키워드 추가 ➔ 어노테이션 + 기존 경고 억제 어노테이션을 새로운 키워드로 취급하고, 잘못된 문법으로 발생하는 코틀린 언어적 
 경고를 억제합니다. Kotlin Compiler Plugin

Slide 8

Slide 8 text

이런 경고를 억제하고(정상 컴파일 가능하도록), 
 컴파일 시점에 lambda의 argument 순서를 @NamedParam에 맞게 바꾸도록 할 수 있다!

Slide 9

Slide 9 text

그래서 배우면 좋을까?

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

배워도 쓸 곳이 거의 없다. 그래서 이 발표에서는 간단하게만 소개합니다

Slide 12

Slide 12 text

성빈은 왜 배웠을까? “KSP의 단점을 극복하고 싶었다” 성빈의 경우 default argument를 복사하면서 코드 생성이 필요했음. KSP는 declaration 영역만 접근이 가능하기에,
 default argument와 같은 expression은 접근이 불가능함. -> default argument의 expression을 알 수 없어, 성빈이 원하는 기능을 KSP로는 구현할 수 없음 ->

Slide 13

Slide 13 text

Kotlin Compiler Plugin은 
 declaration/expression 모두 자유롭게 접근 가능 성빈은 왜 배웠을까?

Slide 14

Slide 14 text

Kotlin Compiler Plugin의 기본 구조 알아보기 FIR / IR

Slide 15

Slide 15 text

“Intermediate Representation”

Slide 16

Slide 16 text

Kotlin Multiplatform Android Web Desktop iOS

Slide 17

Slide 17 text

Intermediate Representation Source Code와 Machine Code의 중간 표현. 대부분의 컴파일러는 IR을 사용해서 Source ⭤ Machine 간의 중복 코드/작업을 최소화한다. (하나의 IR만 관리하면 모든 Machine Code에 반영됨)

Slide 18

Slide 18 text

IrFile 파일 정의를 의미하는 IR class ⭢ 최상위 부모 IR class IrFunction 함수 정의를 의미하는 IR class IrCall 함수 호출과 같은 Call Expression을 의미하는 IR class Intermediate Representation

Slide 19

Slide 19 text

코드 형태 분석 (= Frontend IR) 우리가 작성한 코드를 섬세한 토큰 단위로 분석하여, 이 API가 올바르게 사용되었는지, 코틀린 문법상 문제는 없는지, 최적화 가능한 요소가 있는지 등, 언어의 표면적인 부분을 검사합니다. 동적 코드 변형 (= IR) 분석된 코드 형태를 보고 다양한 최적화를 적용하거나, 기존에는 없던 새로운 코드를
 추가합니다. 새로운 키워드 추가: 어노테이션 + 기존 경고 억제 (= FIR + IR) 어노테이션을 새로운 키워드로 취급하고, 잘못된 문법으로 발생하는 코틀린 언어적 
 경고를 억제합니다. Kotlin Compiler Plugin

Slide 20

Slide 20 text

코드 형태 분석 (= Frontend IR) 우리가 작성한 코드를 섬세한 토큰 단위로 분석하여, 이 API가 올바르게 사용되었는지, 코틀린 문법상 문제는 없는지, 최적화 가능한 요소가 있는지 등, 언어의 표면적인 부분을 검사합니다. 동적 코드 변형 (= IR) 분석된 코드 형태를 보고 다양한 최적화를 적용하거나, 기존에는 없던 새로운 코드를
 추가합니다. 새로운 키워드 추가: 어노테이션 + 기존 경고 억제 (= FIR + IR) 어노테이션을 새로운 키워드로 취급하고, 잘못된 문법으로 발생하는 코틀린 언어적 
 경고를 억제합니다. Kotlin Compiler Plugin

Slide 21

Slide 21 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println(a + b)
 } plus

Slide 22

Slide 22 text

package land.sungbin.calculation

 fun plus(a: Int, b: Int) {
 println(a + b)
 } IrFile

Slide 23

Slide 23 text

package land.sungbin.calculation

 fun plus(a: Int, b: Int) {
 println(a + b)
 } IrFile [IrPackageFragment] land.sungbin.calculation

Slide 24

Slide 24 text

package fun land.sungbin.calculation

 plus(a: Int, b: Int) {
 println(a + b)
 } IrFile [IrPackageFragment] land.sungbin.calculation [IrFunction] name: plus

Slide 25

Slide 25 text

package fun land.sungbin.calculation

 (a: Int ) plus , b: Int {
 println(a + b)
 } IrFile [IrPackageFragment] land.sungbin.calculation [IrFunction] name: plus [IrValueParameter] name: a, type: kotlin.Int

Slide 26

Slide 26 text

package fun land.sungbin.calculation

 (a: Int, b: Int) plus {
 println(a + b)
 } IrFile [IrPackageFragment] land.sungbin.calculation [IrFunction] name: plus [IrValueParameter] name: a, type: kotlin.Int [IrValueParameter] name: b, type: kotlin.Int

Slide 27

Slide 27 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 } plus println(a + b)
 IrFile [IrPackageFragment] land.sungbin.calculation [IrFunction] name: plus [IrValueParameter] name: a, type: kotlin.Int [IrValueParameter] name: b, type: kotlin.Int IrBlockBody

Slide 28

Slide 28 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println( )
 } plus a + b IrFile [IrPackageFragment] land.sungbin.calculation [IrFunction] name: plus [IrValueParameter] name: a, type: kotlin.Int [IrValueParameter] name: b, type: kotlin.Int IrBlockBody [IrCall] symbol: kotlin.io.println value argument: ...

Slide 29

Slide 29 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println(a + b)
 } plus IrFile [IrPackageFragment] land.sungbin.calculation [IrFunction] name: plus [IrValueParameter] name: a, type: kotlin.Int [IrValueParameter] name: b, type: kotlin.Int IrBlockBody [IrCall] symbol: kotlin.io.println value argument: ... [IrCall] symbol: kotlin.Int.plus dispatch receiver: IrGetValue(target: a) value argument: IrGetValue(target: b)

Slide 30

Slide 30 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println(a + b)
 } plus MODULE_FRAGMENT name:test-module FILE fqName:land.sungbin.calculation fileName:/TestSource.kt FUN name:plus visibility:public modality:FINAL <> (a:kotlin.Int, b:kotlin.Int) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Int VALUE_PARAMETER name:b index:1 type:kotlin.Int BLOCK_BODY CALL 'public final fun println (message: kotlin.Int): kotlin.Unit [inline] declared in kotlin.io' type=kotlin.Unit origin=null message: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS $this: GET_VAR 'a: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null other: GET_VAR 'b: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null

Slide 31

Slide 31 text

package land.sungbin.calculation

 fun plus(a: Int, b: Int) {
 println(a + b)
 } MODULE_FRAGMENT name:test-module FILE fqName:land.sungbin.calculation fileName:/TestSource.kt FUN name:plus visibility:public modality:FINAL <> (a:kotlin.Int, b:kotlin.Int) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Int VALUE_PARAMETER name:b index:1 type:kotlin.Int BLOCK_BODY CALL 'public final fun println (message: kotlin.Int): kotlin.Unit [inline] declared in kotlin.io' type=kotlin.Unit origin=null message: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS $this: GET_VAR 'a: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null other: GET_VAR 'b: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null

Slide 32

Slide 32 text

package fun land.sungbin.calculation

 (a: Int, b: Int) plus {
 println(a + b)
 } MODULE_FRAGMENT name:test-module FILE fqName:land.sungbin.calculation fileName:/TestSource.kt FUN name:plus visibility:public modality:FINAL <> (a:kotlin.Int, b:kotlin.Int) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Int VALUE_PARAMETER name:b index:1 type:kotlin.Int BLOCK_BODY CALL 'public final fun println (message: kotlin.Int): kotlin.Unit [inline] declared in kotlin.io' type=kotlin.Unit origin=null message: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS $this: GET_VAR 'a: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null other: GET_VAR 'b: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null

Slide 33

Slide 33 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println( )
 } plus a + b MODULE_FRAGMENT name:test-module FILE fqName:land.sungbin.calculation fileName:/TestSource.kt FUN name:plus visibility:public modality:FINAL <> (a:kotlin.Int, b:kotlin.Int) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Int VALUE_PARAMETER name:b index:1 type:kotlin.Int BLOCK_BODY CALL 'public final fun println (message: kotlin.Int): kotlin.Unit [inline] declared in kotlin.io' type=kotlin.Unit origin=null message: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS $this: GET_VAR 'a: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null other: GET_VAR 'b: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null

Slide 34

Slide 34 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println( + )
 } plus a b MODULE_FRAGMENT name:test-module FILE fqName:land.sungbin.calculation fileName:/TestSource.kt FUN name:plus visibility:public modality:FINAL <> (a:kotlin.Int, b:kotlin.Int) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Int VALUE_PARAMETER name:b index:1 type:kotlin.Int BLOCK_BODY CALL 'public final fun println (message: kotlin.Int): kotlin.Unit [inline] declared in kotlin.io' type=kotlin.Unit origin=null message: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS $this: GET_VAR 'a: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null other: GET_VAR 'b: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null

Slide 35

Slide 35 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println(a + )
 } plus b MODULE_FRAGMENT name:test-module FILE fqName:land.sungbin.calculation fileName:/TestSource.kt FUN name:plus visibility:public modality:FINAL <> (a:kotlin.Int, b:kotlin.Int) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Int VALUE_PARAMETER name:b index:1 type:kotlin.Int BLOCK_BODY CALL 'public final fun println (message: kotlin.Int): kotlin.Unit [inline] declared in kotlin.io' type=kotlin.Unit origin=null message: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS $this: GET_VAR 'a: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null other: GET_VAR 'b: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null

Slide 36

Slide 36 text

package fun land.sungbin.calculation

 (a: Int, b: Int) {
 println(a + b)
 } plus MODULE_FRAGMENT name:test-module FILE fqName:land.sungbin.calculation fileName:/TestSource.kt FUN name:plus visibility:public modality:FINAL <> (a:kotlin.Int, b:kotlin.Int) returnType:kotlin.Unit VALUE_PARAMETER name:a index:0 type:kotlin.Int VALUE_PARAMETER name:b index:1 type:kotlin.Int BLOCK_BODY CALL 'public final fun println (message: kotlin.Int): kotlin.Unit [inline] declared in kotlin.io' type=kotlin.Unit origin=null message: CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS $this: GET_VAR 'a: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null other: GET_VAR 'b: kotlin.Int declared in land.sungbin.calculation.plus' type=kotlin.Int origin=null

Slide 37

Slide 37 text

data class val val fun val MyData( : Int, : Int) () { data = MyData( , ).toString() println(data) } a b main 1 2 // MyData(a=1, b=2)

Slide 38

Slide 38 text

class val val fun var for in ${ } ${ } return fun val MyData( : Int, : Int) { (): String {
 result = result += (param ) { result += param. param. }
 result += result
 } } () { data = MyData( , ).toString() println(data) } a b valueParameters name value toString main ""
 "MyData("
 " = , "
 ")"
 // [Parameter(name = "a", value = a.toString()), ..]
 // MyData(a=1, b=2) 1 2

Slide 39

Slide 39 text

class val val fun MyData( : Int, : Int) { (): String {
 
 } } a b toString fun main() { val data = MyData(1, 2).toString() println(data) // MyData(a=1, b=2) } [IrFunction] name: toString, type: kotlin.String

Slide 40

Slide 40 text

class val val fun var MyData( : Int, : Int) { (): String {
 result = } } a b toString "" 
 fun main() { val data = MyData(1, 2).toString() println(data) // MyData(a=1, b=2) } [IrFunction] name: toString, type: kotlin.String [IrVariable] name: result, type: kotlin.String

Slide 41

Slide 41 text

class val val fun MyData( : Int, : Int) { (): String {
 result += } } a b toString var result = ""
 fun main() { val data = MyData(1, 2).toString() println(data) // MyData(a=1, b=2) } "MyData("

 [IrFunction] name: toString, type: kotlin.String [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result

Slide 42

Slide 42 text

class val val fun for in MyData( : Int, : Int) { (): String {
 (param ) {
 
 } 
 } } a b valueParameters toString var result = ""
 result += "MyData("
 fun main() { val data = MyData(1, 2).toString() println(data) // MyData(a=1, b=2) } [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result

Slide 43

Slide 43 text

class val val fun for in ${ } ${ } MyData( : Int, : Int) { (): String {
 (param ) {
 result += param. param. }

 } } a b valueParameters name value toString var result = ""
 result += "MyData("
 fun main() { val data = MyData(1, 2).toString() println(data) // MyData(a=1, b=2) } " = , "
 [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result

Slide 44

Slide 44 text

class val val fun MyData( : Int, : Int) { (): String {
 result += } } a b toString var result = ""
 result += "MyData("
 for (param in valueParameters) {
 result += "${param.name}=${param.value}, "
 }
 fun main() { val data = MyData(1, 2).toString() println(data) // MyData(a=1, b=2) } ")"

 [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result

Slide 45

Slide 45 text

class val val fun return MyData( : Int, : Int) { (): String {
 result
 } } a b toString var result = ""
 result += "MyData("
 for (param in valueParameters) {
 result += "${param.name}=${param.value}, "
 }
 result += ")"
 fun main() { val data = MyData(1, 2).toString() println(data) // MyData(a=1, b=2) } [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result

Slide 46

Slide 46 text

class val val fun var for in ${ } ${ } return fun val MyData( : Int, : Int) { (): String {
 result = result += (param ) {
 result += param. param. }
 result += result
 } } () { data = MyData( , ).toString() println(data) } a b valueParameters name value toString main ""
 "MyData("
 " = , "
 ")"
 1 2 // MyData(a=1, b=2) [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result

Slide 47

Slide 47 text

[IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result fun val buildDataClassToStringReturn(
 clazz: IrClass,
 parameters: List,
 ) {
 result = IrVariable( , )

 } name = type = "result" "kotlin.String" IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst(clazz.name + "("))
 .dispatchReceiver(result)

 IrForLoop(target = parameters, name = "param")
 .addBodyStatement(
 IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst("\${param.name}=\${param.value}, "))
 .dispatchReceiver(result)
 )

 IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst(")"))
 .dispatchReceiver(result)

 IrReturn(result)


Slide 48

Slide 48 text

[IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result fun val buildDataClassToStringReturn(
 clazz: IrClass,
 parameters: List,
 ) {
 result = IrVariable( , )

 IrCall.fromSymbol( )
 . (IrConst(clazz. + ))
 . (result)

 } name = type = "result" "kotlin.String" "kotlin.String.plusAssign" "(" addValueArgument dispatchReceiver name IrForLoop(target = parameters, name = "param")
 .addBodyStatement(
 IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst("\${param.name}=\${param.value}, "))
 .dispatchReceiver(result)
 )

 IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst(")"))
 .dispatchReceiver(result)

 IrReturn(result)


Slide 49

Slide 49 text

[IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result fun val buildDataClassToStringReturn(
 clazz: IrClass,
 parameters: List,
 ) {
 result = IrVariable( , )

 IrCall.fromSymbol( )
 . (IrConst(clazz. + ))
 . (result)

 IrForLoop( parameters, )
 } name = type = target = name = "result" "kotlin.String" "kotlin.String.plusAssign" "(" "param" addValueArgument dispatchReceiver name .addBodyStatement(
 IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst("\${param.name}=\${param.value}, "))
 .dispatchReceiver(result)
 )

 IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst(")"))
 .dispatchReceiver(result)

 IrReturn(result)


Slide 50

Slide 50 text

fun val \$ \$ buildDataClassToStringReturn(
 clazz: IrClass,
 parameters: List,
 ) {
 result = IrVariable( , )

 IrCall.fromSymbol( )
 . (IrConst(clazz. + ))
 . (result)

 IrForLoop( parameters, )
 .addBodyStatement(
 IrCall.fromSymbol( )
 . (IrConst( ))
 . (result)
 )

 } name = type = target = name = "result" "kotlin.String" "kotlin.String.plusAssign" "(" "param" "kotlin.String.plusAssign" " {param.name}= {param.value}, " addValueArgument dispatchReceiver addValueArgument dispatchReceiver name IrCall.fromSymbol("kotlin.String.plusAssign")
 .addValueArgument(IrConst(")"))
 .dispatchReceiver(result)

 IrReturn(result)
 [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result

Slide 51

Slide 51 text

fun val \$ \$ buildDataClassToStringReturn(
 clazz: IrClass,
 parameters: List,
 ) {
 result = IrVariable( , )

 IrCall.fromSymbol( )
 . (IrConst(clazz. + ))
 . (result)

 IrForLoop( parameters, )
 .addBodyStatement(
 IrCall.fromSymbol( )
 . (IrConst( ))
 . (result)
 )

 IrCall.fromSymbol( )
 . (IrConst( ))
 . (result)

 } name = type = target = name = "result" "kotlin.String" "kotlin.String.plusAssign" "(" "param" "kotlin.String.plusAssign" " {param.name}= {param.value}, " "kotlin.String.plusAssign" ")" addValueArgument dispatchReceiver addValueArgument dispatchReceiver addValueArgument dispatchReceiver name IrReturn(result)
 [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result

Slide 52

Slide 52 text

fun val \$ \$ buildDataClassToStringReturn(
 clazz: IrClass,
 parameters: List,
 ) {
 result = IrVariable( , )

 IrCall.fromSymbol( )
 . (IrConst(clazz. + ))
 . (result)

 IrForLoop( parameters, )
 .addBodyStatement(
 IrCall.fromSymbol( )
 . (IrConst( ))
 . (result)
 )

 IrCall.fromSymbol( )
 . (IrConst( ))
 . (result)

 IrReturn(result)
 } name = type = target = name = "result" "kotlin.String" "kotlin.String.plusAssign" "(" "param" "kotlin.String.plusAssign" " {param.name}= {param.value}, " "kotlin.String.plusAssign" ")" addValueArgument dispatchReceiver addValueArgument dispatchReceiver addValueArgument dispatchReceiver name [IrFunction] name: toString, type: kotlin.String [IrForLoop] target: MyData#valueParameters, name: param [IrVariable] name: result, type: kotlin.String [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “MyData(“) dispatch receiver: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst(value: “)“) dispatch receiver: result [IrReturn] target: result [IrCall] symbol: kotlin.String.plusAssign value argument: IrConst( value: “${param.name}=${param.value}, “ ) dispatch receiver: result

Slide 53

Slide 53 text

class val val fun var for in ${ } ${ } return MyData( : Int, : Int) { (): String {
 result = result += (param ) {
 result += param. param. }
 result += result
 } } a b valueParameters name value // 우리가 IR로 직접 만든 toString() toString ""
 "MyData("
 " = , "
 ")"


Slide 54

Slide 54 text

class val val fun var ${ } ${ } return MyData( : Int, : Int) { (): String {
 result = result += result += a.toString() result += b.toString() result += result
 } } a b // 코틀린 컴파일러가 만드는 toString() toString ""
 "MyData("
 "a= , " "b= "
 ")"


Slide 55

Slide 55 text

fun val for in ( clazz: IrClass, parameters: List, ) {
 irConcat = IrStringConcatenation()
 irConcat. (IrString(clazz.name + ))

 (param parameters) {
 irConcat. (IrString(param.name + ))
 irConcat. (IrGetValue(param) + )
 }

 irConcat. (IrString( ))
 IrReturn(irConcat)
 } generateToStringMethodBody addArgument addArgument addArgument addArgument "(" "=" ", " ")" // 핵심 코드로 추상화되었습니다. // https://is.gd/kt_dc_st 에서 원본 코드 확인 가능

Slide 56

Slide 56 text

Intermediate Representation Source Code와 Machine Code의 중간 표현. 대부분의 컴파일러는 IR을 사용해서 Source ⭤ Machine 간의 중복 코드/작업을 최소화한다. (하나의 IR만 관리하면 모든 Machine Code에 반영됨)

Slide 57

Slide 57 text

나만의 Kotlin Compiler Plugin 만들어 보기 IR Transformation

Slide 58

Slide 58 text

private fun fun val fun (): String = () { println(currentFunctionName()) lambda = { println(currentFunctionName()) } lambda.invoke() otherFunction() } () { println(currentFunctionName()) } currentFunctionName main otherFunction TODO() // "main" 출력 // "" 출력 // "otherFunction" 출력

Slide 59

Slide 59 text

jisungbin/KotlinCompilerPluginTasteSample

Slide 60

Slide 60 text

끝까지 함께 해주셔서 감사합니다! Finish

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

drewhamilton/Poko

Slide 63

Slide 63 text

Jake Wharton/cite

Slide 64

Slide 64 text

2025. 03. 22. 각 상황 별로 Compose Compiler가 만드는 코드, 최적화하는 코드 “모두” 알아보기 이때도 와주실거죠?