Kotlin компиляция функции с примитивными типами - PullRequest
0 голосов
/ 07 января 2019

Экспериментируя со способом компиляции Kotlin в байт-код JVM, я построил этот простой скрипт:

package testFunctions

import java.io.StringWriter
import java.io.PrintWriter

class ToHex : (Int) -> String {
    override fun invoke(p: Int): String {
        val e = Exception()
        val stackTrace = with(StringWriter()) {
            with(PrintWriter(this)) {
                e.printStackTrace(this)
                flush()
            }
            toString()
        }
        println(stackTrace)
        return p.toString(16).toUpperCase()
    }
}

typealias IntToStringFunction = (Int) -> String

fun main(args: Array<String>) {
    val toHex = ToHex()
    println("154 to hex: ${toHex(154)}")

    val toHexLambda: (Int) -> String = { p ->
        val e = Exception()
        val stackTrace = with(StringWriter()) {
            with(PrintWriter(this)) {
                e.printStackTrace(this)
                flush()
            }
            toString()
        }
        println(stackTrace)
        p.toString(16).toUpperCase()
    }
    println("155 to hex: ${toHexLambda(155)}")

    val toHexAlias = toHexLambda as IntToStringFunction
    println("156 to hex: ${toHexAlias(156)}")
}

Глядя на сгенерированный байт-код, я увидел, что оба класса testFunctions.ToHex и testFunctions.TestFunctionsKt$main$toHexLambda$1 скомпилированы для реализации kotlin.jvm.functions.Function1 и определяют общедоступный

final String invoke(int p)

метод. Поскольку этот метод явно не переопределяет абстрактный, определенный в интерфейсе, компилятор добавляет синтаксический мост с сигнатурой

/* syntethic bridge */ Object invoke(Object p)

Теперь, глядя на то, как эти вызовы связаны в основной функции, я заметил следующее поведение:

  1. toHex (154) компилируется с вызовом метода invoke (int p), поэтому без каких-либо ограничений;
  2. toHexLambda (155) и toHexAlias ​​(156) компилируются с вызовом invoke (Object p), поэтому параметры помещаются в целочисленные ссылки перед фактическим вызовом метода.

Это поведение очевидно из стековых трасс, напечатанных в выходных данных программы:

java.lang.Exception
    at testFunctions.ToHex.invoke(testFunctions.kt:8)
    at testFunctions.TestFunctionsKt.main(testFunctions.kt:25)

154 to hex: 9A
java.lang.Exception
    at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt:28)
    at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt)
    at testFunctions.TestFunctionsKt.main(testFunctions.kt:39)

155 to hex: 9B
java.lang.Exception
    at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt:28)
    at testFunctions.TestFunctionsKt$main$toHexLambda$1.invoke(testFunctions.kt)
    at testFunctions.TestFunctionsKt.main(testFunctions.kt:42)

156 to hex: 9C

Как видите, и testFunctions.kt:39, и testFunctions.kt:42 вызывают метод синтаксического моста, который делегирует фактическую реализацию метода.

Итак, теперь мой вопрос: учитывая, что при использовании функции reified ToHex компилятор может оптимизировать вызов, используя метод, который использует тип примитива, и учитывая, что тип функции (Int) -> String не может принять null В качестве входных данных, почему компилятор Kotlin не может понять, что можно избежать вызова метода syntethic bridge?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...