GDG DevFest 2017 - Inspections of Kotlin implementations by Bytecode.
세션 이후 "Kotlin은 Java의 wrapper인가요?" 라는 질문을 몇번 받았습니다.
—
답변: 그렇지 않습니다.
특정한 언어로 구현된 코드는 파싱을 거쳐 추상화된 형태(AST)와 추가 정보들을 가지는 1차적인 결과물로 처리됩니다. 보통 이런 역할을 하는 것은 컴파일러에서 전단부(frontend)라고 호칭하며 이러한 AST 등의 결과물은 대상 머신이나 플랫폼에 맞추어 처리됩니다.
이를 바로 실행하면 인터프리터라고 하지만, 실행 가능한 형태(Executable)로 생성하는 경우라면 컴파일러 후단부(Backend)가 이를 수행합니다.
백엔드의 타겟 코드는 충분히 다양한 대상을 다룰 수 있으므로, 우리가 다양한 백엔드 구현을 통해 동일 코드를 멀티 플랫폼을 대상으로 실행할 수 있도록 할 수 있는 것입니다.
코틀린 역시 대상으로 하는 플랫폼(과 머신)은 현재 다음과 같은 실행 가능한 형태를 지원하고 있습니다. (물론 아직 모든 타겟이 완벽하지는 않겠죠.)
1. Bytecode 포맷에 따른 JVM(안드로이드 포함)
2. JavaScript에 의한 브라우저나 Node.js
3. llvm을 이용하여 여러 타겟의 네이티브 코드
이 자료는 이 중 1번을 기반으로 디컴파일된 코드를 살펴보고 코틀린의 코드 생성 목적이나 언어 설계의 원인(어떤 painpoint)를 찾아보는 과정의 일부였을 뿐입니다.
언어는 항상 요구되는 표현을 위해 가장 적합한 형태로 변화해나갑니다. 프로그래밍 언어는 비교적 단기간에 만들어지는 언어이고, 그에 따라 특정 사람과 집단의 목적에 충실합니다. 다만, 이 관점에서 봤을 때도 Kotlin이 Java의 wrapper로써 설계되었을 것보다는 다양한 타겟 플랫폼이 고려되고 있는 하나의 프로그래밍 언어로 받아들여 주시기를 바랍니다. :)
11. package com.cwdoh.devfest2017
class Session {
val speaker = "cwdoh"
val title: String
= "Kotlin: How it works"
var room: Int? = null
fun description()
= "$speaker's talk: '$title' at room $room"
}
13. package com.cwdoh.devfest2017
class Session {
val speaker = "cwdoh"
val title: String
= "Kotlin: How it works"
var room: Int? = null
fun description()
= "$speaker's talk: '$title' at room $room"
}
14. class Session {
var name = "cwdoh"
}
public final class Session {
@NotNull
private String name = "cwdoh";
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
}
15. class Session {
val name = "cwdoh"
}
public final class Session {
@NotNull
private final String name = "cwdoh";
@NotNull
public final String getName() {
return this.name;
}
}
16. class Session {
val speaker = "cwdoh"
fun description() {
val talks = "$speaker's talks"
println(talks)
}
}
public final class Session {
@NotNull
private final String speaker = "cwdoh";
@NotNull
public final String getSpeaker() {
return this.speaker;
}
public final void description() {
String talks = "" + this.speaker + "'s talks";
System.out.println(talks);
}
}
18. package com.cwdoh.devfest2017
class Session {
val speaker = "cwdoh"
val title: String
= "Kotlin: How it works"
var room: Int? = null
fun description()
= "$speaker's talk: '$title' at room $room"
}
19. class Session {
var name: String = "cwdoh"
}
public final class Session {
@NotNull
private String name = "cwdoh";
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
}
public static void checkParameterIsNotNull(Object value, String paramName)
{
if (value == null) {
throwParameterIsNullException(paramName);
}
}
20. class Session {
fun hello(name: String) = "hello, " + name
}
public final class Session {
@NotNull
public final String hello(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
return "hello, " + name;
}
}
public static void checkParameterIsNotNull(Object value, String paramName)
{
if (value == null) {
throwParameterIsNullException(paramName);
}
}
21. class Session {
fun hello(name: String) = "hello, " + name
fun print() {
val name: String = "cwdoh"
print(hello(name))
}
}
public final class Session {
@NotNull
public final String hello(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
return "hello, " + name;
}
public final void print() {
String name = "cwdoh";
String var2 = this.hello(name);
System.out.print(var2);
}
}
22. class Session {
fun hello(name: String) = "hello, " + name
fun print() {
val name: String? = null
print(hello(name!!))
}
}
public final class Session {
@NotNull
public final String hello(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
return "hello, " + name;
}
public final void print() {
String name = (String)null;
Intrinsics.throwNpe();
String var2 = this.hello(name);
System.out.print(var2);
}
}
NullPointerException? 😎
24. package com.cwdoh.devfest2017
class Session {
val speaker = "cwdoh"
val title: String
= "Kotlin: How it works"
var room: Int? = null
fun description()
= "$speaker's talk: '$title' at room $room"
}
25. class Session {
val speaker = "cwdoh"
val title: String = "Kotlin: How it works"
var room: Int? = null
fun description() = "$speaker's talk: '$title' at room $room"
}
public final class Session {
@NotNull
private final String speaker = "cwdoh";
@NotNull
private final String title = "Kotlin: How it works";
@Nullable
private Integer room;
…
@NotNull
public final String description() {
return "" + this.speaker + "'s talk: ‘"
+ this.title + "' at room " + this.room;
}
}
29. interface Interface
open class OpenClass
class ChildClass: OpenClass(), Interface
fun test() { val child = ChildClass() }
public final class ChildClass
extends OpenClass implements Interface {}
public interface Interface {}
public class OpenClass {}
public final class SimpleClassKt {
public static final void test() {
new ChildClass();
}
}
31. class Person1 constructor(name: String)
class Person2(name: String)
public final class Person1 {
public Person1(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
}
}
public final class Person2 {
public Person2(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
}
}
32. class Person constructor(val name: String)
public final class Person {
@NotNull
private final String name;
@NotNull
public final String getName() {
return this.name;
}
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
}
}
33. class Person constructor(val name: String) {
val greetings: String
init { greetings = "hello, $name” }
}
public final class Person {
@NotNull private String greetings;
@NotNull private final String name;
@NotNull public final String getGreetings() { return this.greetings; }
@NotNull public final String getName() { return this.name; }
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.greetings = "hello, " + this.name;
}
}
34. class Person constructor(val name: String) {
val greetings: String
var age: Int = null
constructor(name: String, age: Int): this(name) { this.age = age }
init { greetings = "hello, $name” }
}
public final class Person {
@NotNull
private final String greetings;
private int age;
…
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = ((Number)null).intValue();
this.greetings = "hello, " + this.name;
}
public Person(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
this(name);
this.age = age;
}
}
36. open class Parent {
private val a = println("Parent.a")
constructor(arg: Unit=println("Parent primary constructor arg")) {
println("Parent primary constructor")
}
init { println("Parent.init") }
private val b = println("Parent.b")
}
class Child : Parent {
val a = println("Child.a")
init { println("Child.init 1") }
constructor(arg: Unit=println("Child primary constructor arg")) : super() {
println("Child primary constructor")
}
val b = println("Child.b")
constructor(arg: Int, arg2:Unit= println("Child secondary constructor arg")): this() {
println("Child secondary constructor")
}
init { println("Child.init 2") }
}
fun main(args: Array<String>) {
Child(1)
}
Child secondary constructor arg
Child primary constructor arg
Parent primary constructor arg
Parent.a
Parent.init
Parent.b
Parent primary constructor
Child.a
Child.init 1
Child.b
Child.init 2
Child primary constructor
Child secondary constructor
39. class Props {
var size: Int = 0
val isEmpty: Boolean
get() = this.size == 0
}
public final class Props {
private int size;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.size == 0;
}
}
40. class Props {
var age: Int = 0
set(value: Int) {
age = if (value < 0) 0 else value
}
}
public final class Props {
private int age;
public final int getAge() {
return this.age;
}
public final void setAge(int value) {
this.setAge(value < 0? 0:value);
}
}
41. class Props {
var age: Int = 0
private set
}
public final class Props {
private int age;
public final int getAge() {
return this.age;
}
private final void setAge(int var1) {
this.age = var1;
}
}
44. class MainActivity : AppCompatActivity() {
private var mWelcomeTextView: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mWelcomeTextView
= findViewById(R.id.msgView) as TextView
}
}
45. class MainActivity : AppCompatActivity() {
private lateinit var mWelcomeTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mWelcomeTextView
= findViewById(R.id.msgView) as TextView
}
}
46. class MainActivity : AppCompatActivity() {
private val mWelcomeTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// ?????
// mWelcomeTextView =
// findViewById(R.id.msgView) as TextView
}
}
47. class MainActivity : AppCompatActivity() {
private val messageView : TextView by lazy {
// messageView
findViewById(R.id.message_view) as TextView
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun onSayHello() {
messageView.text = "Hello"
}
}
49. class Delegate {
operator fun getValue(
thisRef: Any?,
property: KProperty<*>
): String {
// do something
// return value
}
operator fun setValue(
thisRef: Any?,
property: KProperty<*>, value: String
) {
// do something
// assign
}
}
52. class Demo {
val myName : String by lazy { "John" }
}
public final class Demo {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{ … };
@NotNull
private final Lazy myName$delegate;
@NotNull
public final String getMyName() {
Lazy var1 = this.myName$delegate;
KProperty var3 = $$delegatedProperties[0];
return (String)var1.getValue();
}
public Demo() {
this.myName$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
}
53. class Demo {
val myName : String by lazy { getNameFromPreference() }
} initializerdelegate
61. interface Base {
fun printX()
}
class BaseImpl(val x: Int) : Base {
override fun printX() { print(x) }
}
62. interface A {
fun hello(): String
}
class B : A {
override fun hello() = "Hello!!"
}
class C : A by B()
63. public interface A {
@NotNull
String hello();
}
public final class B implements A {
@NotNull
public String hello() {
return "Hello!!";
}
}
public final class C implements A {
// $FF: synthetic field
private final B $$delegate_0 = new B();
@NotNull
public String hello() {
return this.$$delegate_0.hello();
}
}
66. fun String.hello() : String {
return "Hello, $this"
}
fun main(args: Array<String>) {
val whom = "cwdoh"
println(whom.hello())
}
// Result
Hello, cwdoh
68. open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
class Demo {
fun run() {
printFoo(D())
}
}
public class C {}
public final class D extends C {}
public final class SimpleClassKt {
@NotNull
public static final String foo(@NotNull C $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return "c";
}
@NotNull
public static final String foo(@NotNull D $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return "d";
}
public static final void printFoo(@NotNull C c) {
Intrinsics.checkParameterIsNotNull(c, "c");
String var1 = foo(c);
System.out.println(var1);
}
}
public final class Demo {
public final void run() {
SimpleClassKt.printFoo((C)(new D()));
}
}
69. class Person {
fun hello() {
println("hello!")
}
}
fun Person.hello() {
println(" ?!!")
}
fun main(args: Array<String>)
{
Person().hello()
}
// Result
hello!
public final class Person {
public final void hello() {
String var1 = "hello!";
System.out.println(var1);
}
}
public final class SimpleClassKt {
public static final void hello(@NotNull Person $receiver) {
Intrinsics.checkParameterIsNotNull(
$receiver, "$receiver");
String var1 = " ?!!";
System.out.println(var1);
}
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
(new Person()).hello();
}
}
70. class D {
fun bar() {
println("D.bar()")
}
}
class C {
fun baz() {
println("C.bar()")
}
fun D.foo() {
bar() // calls D.bar
baz() // calls C.baz
}
fun caller(d: D) {
d.foo()
}
}
public final class C {
public final void baz() {
String var1 = "C.bar()";
System.out.println(var1);
}
public final void foo(@NotNull D $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
$receiver.bar();
this.baz();
}
public final void caller(@NotNull D d) {
Intrinsics.checkParameterIsNotNull(d, "d");
this.foo(d);
}
}
public final class D {
public final void bar() {
String var1 = "D.bar()";
System.out.println(var1);
}
}
72. data class Length(var centimeters: Int = 0)
var Length.meters: Float
get() {
return centimeters / 100.0f
}
set(meters: Float) {
this.centimeters = (meters * 100.0f).toInt()
}
73. data class Length(var centimeters: Int = 0)
var Length.meters: Float
get() {
return centimeters / 100.0f
}
set(meters: Float) {
this.centimeters = (meters * 100.0f).toInt()
}
public final class Length {
private int centimeters;
...
}
public final class ExtensionsKt {
public static final float getMeters(
@NotNull Length $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return (float)$receiver.getCentimeters() / 100.0F;
}
public static final void setMeters(
@NotNull Length $receiver, float meters) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
$receiver.setCentimeters((int)(meters * 100.0F));
}
...
}
74. fun Any?.toString(): String
{
if (this == null)
return “null"
return toString()
}
public final class SimpleClassKt {
@NotNull
public static final String
toString(@Nullable Object $receiver) {
return $receiver == null?
"null":$receiver.toString();
}
}
75. fun Any?.toString(): String {
println("Extension is called.")
if (this == null) return "null"
return toString()
}
fun main(args: Array<String>) {
val var1 : Any? = null
println(var1.toString())
val str1 : String? = null
println(str1.toString())
var str2 : String? = "hello"
println(str2.toString())
var str3 : String = "world"
println(str3.toString())
}
public final class SimpleClassKt {
@NotNull
public static final String toString(@Nullable Object $receiver) {
String var1 = "Extension is called.";
System.out.println(var1);
return $receiver == null?"null":$receiver.toString();
}
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, “args");
Object var1 = null;
String str1 = toString(var1);
System.out.println(str1);
str1 = (String)null;
String str2 = toString(str1);
System.out.println(str2);
str2 = "hello";
String str3 = toString(str2);
System.out.println(str3);
str3 = "world";
String var5 = str3.toString();
System.out.println(var5);
}
}
Extension is called.
null
Extension is called.
null
Extension is called.
hello
world
77. fun sing() {
println(" ")
println(" ")
println(" ")
}
fun beatbox() {
println(" ")
}
fun ensemble() {
sing()
beatbox()
sing()
beatbox()
}
public final class SimpleClassKt {
public static final void sing() {
String var0 = " ";
System.out.println(var0);
var0 = " ";
System.out.println(var0);
var0 = " ";
System.out.println(var0);
}
public static final void beatbox() {
String var0 = " ";
System.out.println(var0);
}
public static final void ensemble() {
sing();
beatbox();
sing();
beatbox();
}
}
78. inline fun sing() {
println(" ")
println(" ")
println(" ")
}
fun beatbox() {
println(" ")
}
fun ensemble() {
sing()
beatbox()
sing()
beatbox()
}
public final class SimpleClassKt {
public static final void sing() {
String var1 = " ";
…
}
public static final void beatbox() {
…
}
public static final void ensemble() {
String var0 = " ";
System.out.println(var0);
var0 = " ";
System.out.println(var0);
var0 = " ";
System.out.println(var0);
beatbox();
var0 = " ";
System.out.println(var0);
var0 = " ";
System.out.println(var0);
var0 = " ";
System.out.println(var0);
beatbox();
}
}
79. fun log(message: () -> String) {
println(message())
}
fun test() {
log { "Lorem ipsum dolor sit amet, consectetur ..." }
}
public final class SimpleClassKt {
public static final void log(@NotNull Function0 message) {
Intrinsics.checkParameterIsNotNull(message, "message");
Object var1 = message.invoke();
System.out.println(var1);
}
public static final void test() {
log((Function0)null.INSTANCE);
}
}
80. inline fun trace(message: String) {}
fun doSomething() {
print("I'm doing something!")
trace("doSomething() is doing something!")
}
public final class SimpleClassKt {
public static final void trace(@NotNull String message) {
Intrinsics.checkParameterIsNotNull(message, "message");
}
public static final void doSomething() {
String var0 = "I'm doing something!";
System.out.print(var0);
var0 = "doSomething() is doing something!";
}
}
81. inline fun log(message: () -> String) {
println(message())
}
fun test() {
log { "Lorem ipsum dolor sit amet, consectetur ..." }
}
public final class SimpleClassKt {
public static final void log(@NotNull Function0 message) {
Intrinsics.checkParameterIsNotNull(message, "message");
Object var2 = message.invoke();
System.out.println(var2);
}
public static final void test() {
String var0 = "Lorem ipsum dolor sit amet, consectetur ...";
System.out.println(var0);
}
}
82. inline fun trace(message: ()-> String) {}
fun doSomething() {
trace {
val receiver = "nurimaru"
val sender = "cwdoh"
"$sender wanna give $receiver big thank you for good tips."
}
}
public final class SimpleClassKt {
public static final boolean isDebug() {
return true;
}
public static final void trace(@NotNull Function0 message) {
Intrinsics.checkParameterIsNotNull(message, "message");
}
public static final void doSomething() {
}
}