【kotlinx-metadata】関数の引数に設定された型引数の情報を読み出す

# TL;DR
- 関数の引数の型が型引数だった場合、その型情報は`KmClassifier.TypeParameter`として取得できる
- この`id`は一番外側のクラスから0スタートで内側（クラスの定義 -> 関数の定義）に向けて加算される形で決定される
  - このため、同一クラスに定義された関数間などでは`id`が一致することが有りうる
  - クラスがネストしている場合、例え内側のクラスが`static`に（= `inner class`じゃない形で）定義されていても、外側の型引数は`id`にカウントされる
- このため、型引数の情報を読む場合、以下のような形に実装する必要が有る

```kotlin
val clazz: Class<*> = /* 略 */

// 型を外側に辿っていくことで、clazzのスコープの型引数を全て取得する
val classTypeParameters: List<KmTypeParameter> = generateSequence(clazz) { it.enclosingClass }
    .map { (it.toKotlinClassMetadata() as KotlinClassMetadata.Class).toKmClass() }
    .toList()
    .foldRight(listOf<KmTypeParameter>()) { cur, acc -> acc + cur.typeParameters }

// 関数に定義された型パラメータをclassTypeParametersに加える
val kmFunction: KmFunction = /* 略 */
val allTypeParameters: List<KmTypeParameter> = classTypeParameters + kmFunction.typeParameters

// idでアクセスすることで、当該の型引数が得られる
val classifier: KmClassifier.TypeParameter = /* 略 */
val typeParameter: KmTypeParameter = allTypeParameters[classifier.id]
```

# やりたいこと
`Kotlin`上で、型引数と同じ型を引数として要求するような関数を定義したとします。

```kotlin
fun <T> func(arg: T) { /* ... */ }
```

このような関数を`KmFunction`として取得し、引数の型情報を見ると、`KmClassifier.TypeParameter`が得られていることが分かります。

```kotlin
val kmFunction: KmFunction = /* 先ほど定義したfunc関数の取得処理 */

// 引数情報の確認
kmFunction
    .valueParameters.single()
    .type!!
    .classifier
    .let { it: KmClassifier ->
        it as KmClassifier.TypeParameter // キャストしてもエラーにならない
        println(it.id) // KmClassifier.TypeParameterはidフィールドのみ持っている
    }
```

やりたいことは、この`id`を実際の型引数の情報と紐づけることです。
やり方は冒頭に記述した通りです。

## 検証コード
以下に`id`のカウント方法に関する検証コードを記載します。
依存としては`kotlin-stdlib`と`kotlinx.metadata`(0.3.0)のみ利用しています。

```kotlin
package com.wrongwrong

import kotlinx.metadata.KmClass
import kotlinx.metadata.KmFunction
import kotlinx.metadata.KmTypeParameter
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.jvm.KotlinClassMetadata

class Outer<OuterParameter, DummyParameter> { // DummyParameterは加算を分かりやすくするためのもの
    inner class InnerClass<InnerParameter1> {
        fun f0(outer: OuterParameter, inner: InnerParameter1) {}
        fun <FuncTypeParameter1> f1(inner: InnerParameter1, f1: FuncTypeParameter1) {}
        fun <FuncTypeParameter2> f2(f2: FuncTypeParameter2) {}
    }

    class StaticInnerClass<InnerParameter2> {
        fun f0(inner: InnerParameter2) {} // staticな内部クラスでは外側のクラスの型引数を参照できないため省略
        fun <FuncTypeParameter1> f1(inner: InnerParameter2, f1: FuncTypeParameter1) {}
        fun <FuncTypeParameter2> f2(f2: FuncTypeParameter2) {}
    }
}

fun printSample(javaClazz: Class<*>) {
    // kotlinx.metadataでの情報読み出し
    val kmClass: KmClass = (javaClazz.toKotlinClassMetadata() as KotlinClassMetadata.Class).toKmClass()
    val f0: KmFunction = kmClass.functions.first { it.name == "f0" }
    val f1: KmFunction = kmClass.functions.first { it.name == "f1" }
    val f2: KmFunction = kmClass.functions.first { it.name == "f2" }

    f0.valueParameters.print("f0")
    f1.valueParameters.print("f1")
    f2.valueParameters.print("f2")
}

fun main() {
    println("--- InnerClass ---")
    printSample(Outer.InnerClass::class.java)

    println()

    println("--- StaticInnerClass ---")
    printSample(Outer.StaticInnerClass::class.java)
}

fun List<KmValueParameter>.print(funcName: String) {
    println(this.joinToString(prefix = "$funcName\n", separator = ",\n") {
        "  paramName: ${it.name}, classifier: ${it.type!!.classifier}"
    })
}
```

### 検証コードの説明
検証コードでは、`Outer`クラス内に`InnerClass`と`StaticInnerClass`の2クラスを定義し、更にその中に`f0` ~ `f2`の3関数を定義して、計6つの関数についてそれぞれの引数の情報を表示しています。

`id`が外側から順に計算されるのであれば、以下のような結果となります。

- `OuterParameter`の`id`は0になる
- `InnerParameter`の`id`は全て2になる
- `FuncTypeParameter`の`id`は全て3になる

### 実行結果
実行結果は以下の通りです。
解説に記述した通りの結果になっていることが分かります。

```
--- InnerClass ---
f0
  paramName: outer, classifier: TypeParameter(id=0),
  paramName: inner, classifier: TypeParameter(id=2)
f1
  paramName: inner, classifier: TypeParameter(id=2),
  paramName: f1, classifier: TypeParameter(id=3)
f2
  paramName: f2, classifier: TypeParameter(id=3)

--- StaticInnerClass ---
f0
  paramName: inner, classifier: TypeParameter(id=2)
f1
  paramName: inner, classifier: TypeParameter(id=2),
  paramName: f1, classifier: TypeParameter(id=3)
f2
  paramName: f2, classifier: TypeParameter(id=3)
```