JavaクラスファイルとScalaSignature

## Introduction
### きっかけ
- Scalaのsealedってどうやって実現されてるんだろう？

### sealed modifier
- sealedをつけたclass, traitは、同一ファイル内でのみ継承できる
- Javaには同じ概念はない（finalは似てるけど）
- コンパイル済みclassファイルをリンクする場合も、sealedは有効

example

```Foo.scala
sealed class Foo
```

```Bar.scala
class Bar extends Foo
```

```bash
$ scalac Foo.scala

$ ls
Bar.scala  Foo.class  Foo.scala

$ scalac Bar.scala
Bar.scala:1: error: illegal inheritance from sealed class Foo
class Bar extends Foo
                  ^
one error found
```

## この記事の目的
- Scalaのsealedがどう実現されているのか分かる

## 環境
- `Scala compiler version 2.11.8 -- Copyright 2002-2016, LAMP/EPFL`
- `java version "1.8.0_77"`

## Javaクラスファイルの基礎
レイアウト

```
ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
```

|name|length|description|
|:--|:--|:--|
|magic|4 bytes|固定値 `0xCAFEBABE`|
|minor_version|2 bytes|JDKのバージョンを表す|
|major_version|2 bytes|同上|
|constant_pool_count|2 bytes|定数プールのエントリ数|
|constant_pool|-|定数プール|
|access_flags|2 bytes|クラス/インターフェース/final/publicなどを表すフラグ|
|this_class|2 bytes|このクラスを表す、定数プール内のインデックス|
|super_class|2 bytes|親クラスを表す、定数プール内のインデックス|
|interfaces_count|2 bytes|このクラスの直接の親インターフェースの数|
|interfaces|-|各親インターフェースを表す定数プール内のインデックスの配列|
|fields_count|2 bytes|このクラスが持つフィールドの数|
|fields|-|フィールドを表す`field_info`の配列|
|methods_count|2 bytes|このクラスが持つメソッドの数|
|methods|-|メソッドを表す`method_info`の配列|
|attributes_count|2 bytes|このclassファイルに格納された属性の数|
|attributes|-|属性を表す`attribute_info`の配列|

### example

```Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("hello, world");
    }
}
```

```
$ javac Hello.java
$ javap -v Hello.class

Classfile /Users/hokada/develop/scala/sealed-example/clean/Hello.class
  Last modified Oct 14, 2016; size 416 bytes
  MD5 checksum 83526ec8f7ebea872a7a2c3b7c8a4d2b
  Compiled from "Hello.java"
public class Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // hello, world
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // Hello
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               Hello.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               hello, world
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               Hello
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public Hello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String hello, world
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 3: 0
        line 4: 8
}
SourceFile: "Hello.java"
```

## attributesに格納できるんじゃ？

- 実際、Scala 2.7以前は`attributes`に格納されてたっぽい

## Scala 2.8以降は？

- `ScalaSignature`アノテーションのbytesという値(String)に格納されている

### なぜアノテーションか
- JVMはカスタムattributeを無視する
- なのでJavaのreflectionではScala特有の情報は取りようがない
- reflectionの利便性のためと、classファイルをもう一度parseする無駄をなくすためっぽい？

`Storage of pickled Scala signatures in class files` より引用

> 3. Why does Scala 2.8 store signatures differently?

> The legacy method for storing signatures as attributes is simultaneously more elegant,
more compact (about 15%) and simpler than that using annotations. However, to access
the pickled signature in attributes requires obtaining and parsing the entire class file.
Because annotations are recognized by the JVM, the new method allows retrieving pickled
signature bytes directly from within a running Scala program by using Java reflection.

### 格納方法
- sealedなど各情報のフラグはバイト列
- 定数プールにバイト列は格納できないので、うまいこと文字列にエンコードしてるっぽい
  - https://github.com/scala/scala/blob/2.11.x/src/reflect/scala/reflect/internal/AnnotationInfos.scala#L132

## Java reflectionでScalaSignatureを取得してみる

というわけで、Javaのreflectionを使って、冒頭の`Foo.class`のScalaSignaturesが取得できるはず

```ShowAnnotation.scala
import java.lang.annotation.Annotation;

public class ShowAnnotation {
    public static void main(String[] args) {
        Annotation[] annots = Foo.class.getAnnotations();

        for(Annotation annotation : annotations){
            System.out.println(annotation);
        }
    }
}
```

```
$ javac ShowAnnotation.java
$ java -cp ~/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.11.8.jar:. ShowAnnotation
@scala.reflect.ScalaSignature(bytes=E1A!
                                        	ai\8
	qP3naRLhh1CA
                    A!"A
\1                      M
Ca"=S:LGO+g
         AA)
```

とれる。

## 参考資料
- [Chapter 4. The class File Format](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html)
- [Storage of pickled Scala signatures in class files](http://www.scala-lang.org/old/sites/default/files/sids/dubochet/Mon,%202010-05-31,%2015:25/Storage%20of%20pickled%20Scala%20signatures%20in%20class%20files.pdf)