言いたいことはそれだけか

KotlinとかAndroidとかが好きです。調べたことをメモします。٩( 'ω' )و

Kotlin Bytecodeを読みたいけど、何から勉強したらいいかわからないあなたへ。ASMのドキュメントを読みましょう。

はじめに

このblog postは半年以上下書き放置していて自分の中では旬がすぎた話題ではありますが、最近Android系日本語技術podcastであるdex.fmのep.66でbytecodeが話題になったので、勿体無い精神で公開します :pray: それではどうぞ。

Kotlin書いているとたまに「これ、JavaのBytecodeだとどう変換されて動いているんだろう?」と思う時がしばしばある。

例えばこれとか

muumuutech.hatenablog.com

で、Android Studioの "Show Kotlin Bytecode" 機能を使ってbytecodeをのぞいてみるんだけど、完全に雰囲気で読んでいたので、わかるようなわからないようなゆるふわな感じだった😇これの意味は?とか聞かれても説明するなんて無理〜〜と言う程度の理解レベル。

何がつらいかっていうと、ググってもいい感じのリファレンスがないというか、まず何を読むべきかが見つけられないという状況😩😩😩 しかし軽い気持ちで呟いた結果、神々が降臨した。圧倒的感謝 :pray: 1

というわけで、Bytecodeに対してゆるふわな理解しかない人が勉強して行った記録を残していく。間違ったこと書いてたら教えてくださいmm

手取り早くbytecodeを理解した人へ

ASM User GuideのAppendixにBytecode instructionsがまとまっているので、ここを読みましょう。

ASMとは?

まずは公式の定義から。

ASM

ASM is an all purpose Java bytecode manipulation and analysis framework. It can be used to modify existing classes or to dynamically generate classes, directly in binary form. ASM provides some common bytecode transformations and analysis algorithms from which custom complex transformations and code analysis tools can be built. ASM offers similar functionality as other Java bytecode frameworks, but is focused on performance. Because it was designed and implemented to be as small and as fast as possible, it is well suited for use in dynamic systems (but can of course be used in a static way too, e.g. in compilers).

KotlinのコンバイラはASMライブラリを使ってbytecodeを生成している。なのでKotlinで生成されたBytecodeを読みたいだけだったら、ASMが何をしてどういう生成物を吐き出すか知っていれば良いっぽい。

余談だけどASMはOSSで、しかもGithubではなくGitlabでhostingされている。

gitlab.ow2.org

User Guide

まずは使う側としてUser Guide読むことにする。めちゃめちゃ長いPDFになっている。

https://asm.ow2.io/asm4-guide.pdf

読んでて面白かった部分を書いていく。なんかたまにルー大柴っぽくなった気がする。気になる人は原文読んでください…

1. Introduction

  • ASMはできるだけ早くて小さくて堅牢というのを目指している
  • ASMライブラリの目的はbyte arrayで表現されるコンパイルずみのJava classesを生成、変換、分析すること
    • class loading processは対象外
  • ASMという名前に意味はない。Cの__asm__ keyword からとっただけ
  • core APIはevent basedなクラス表現
  • tree APIはobject basedなクラス表現
    • この辺 見ると、Kotlin compilerはtreeの方使ってるのかな?という感じがする

Tree API

2. Classes 3. Methods 4. Metadata 5. Backward compatibility の章はCore APIについてだったのでスキップ)

6. Classes

  • tree APIを使ってclassを生成、変換する場合、core APIに比べて約3割多くの時間がかかり、メモリも多く使う
  • そのかわりどんな順序でもclass elementsが生成できるので便利

7. Method

  • methodの中身はInsnListでinstructionが記述されていく
    • こいつがgetOpCode()とかを持っている
    • getNext() があるからjumpが簡単にできる
  • Label, frame, line numberもinstructionではないけどAbstractInsnNodeのサブクラスとして表現される
    • これにより実instructionsの前とかにLabelとかをinsertすることができる

8. Method Analysis

  • ASMで使われるcode analysis techniqueにはdata flow analysiscontrol flow analysisがある
  • data flow analysis
    • methodのexecution frameのstateを計算する。
    • forward analysisとbackward analysisがある
    • stackから値をpopして計算して結果をstackにpush
    • interpreterJVMのように見えるが、違いは可能性がある全てのpath, argumentについてsimulateする
      • manipulated valueは可能性がある値の集合になるのでめっちゃ大きくなる。
      • 例)intergerをP="positive or null", N="negative or null, A="all integers" として表すとIADD instructionはoperandがどちらもPだったらPを返し、どちらもNだったらNを返し、そのほかのケースは全てAを返す
  • control flow analysis
    • methodのcontrol data flow graphを計算し、このgraphを解析する
    • graphはbasic blockにdecompileされる。それぞれのbasic blockは(最初のblockを除き)jumpの対象となりうる。
  • Interface and components
    • stackからのpopとかpushはframeworkで、valueをcombineしたり集合の計算とかはInterpreterとかValueと行ったuserが定義したsubclassで行われる
  • Basic Data Flow Analysis
definition means
UNINITIALIZED_VALUE all possible values
INT_VALUE all int, short, byte, boolean or char values
FLOAT_VALUE all float values
LONG_VALUE “all long values”
DOUBLE_VALUE “all double values”
REFERENCE_VALUE “all object and array values”
RETURNADDRESS_VALUE is used for subroutines (see Appendix A.2)
  • Appendix
    • 上記にも書いたが、ここにinstructionsとその操作例がまとまっているのでここを見ればだいたいbytecodeが読めるようになっている

Developer Guide読んでいく

こっちの方が短いけど難しい。3割くらいしか理解できていない気がするのでさらっとメモだけ。 ASM - Developer Guide

Main Algorithms

  • class loader
    • constant poolと(constructorの中の)bootstrap methodをparseする
    • classをparseする
    • class attributeをparseする
    • class attributeに対応したvisit methodをコールする
    • fieldについて上記と同じようなことをする
    • methodについて同上
      • ただしattributeはparseした後local variableにstore
      • labelを探してstore
      • instructionsをparse
  • 3.4.3 An example
    • 要約するのがむずいのでこの実例を見てくれたらなんとなくわかる





以上、わかるようなわからないような状態から一歩踏み出すためのリファレンス集でした。


  1. こう言う時本当に投げ銭で感謝のお気持ちを表明したいのでみんなKyashとかのリンクをTwitter bioとかに貼っておいてほしい…