Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

ScalaのHelloWorldをjavapしてみた

jyukutyo.hatenablog.com

ということを言ったので、Scalaのコードもjavapしようと思いました。 もちろんscalacしてからjavapしてもよいのですが、ScalaはなんとREPLからjavapできると知りました。

REPL で使える :javap コマンドのオプションはコマンドラインの javap(1)... - tnoda-scala

すばらしい記事だと感じました。ありがとうございます!

なので、REPLでHelloWorldを作成してjavapしたいと思います。

scala> object HelloWorldInScala {
     |   def main(args: Array[String]) : Unit = {
     |     printf("Hello World!")
     |   }
     | }
defined object HelloWorldInScala

REPLでは:javapで実行できます。

scala> :javap HelloWorldInScala
バイナリ・ファイルHelloWorldInScalaにHelloWorldInScala$が含まれています
  Size 912 bytes
  MD5 checksum 5dfd2db5d76bd3264d1907e6a92a5fbb
  Compiled from "<console>"
public class HelloWorldInScala$
  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Utf8               HelloWorldInScala$
   #2 = Class              #1             // HelloWorldInScala$
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               <console>
   #6 = Utf8               MODULE$
   #7 = Utf8               LHelloWorldInScala$;
   #8 = Utf8               <clinit>
   #9 = Utf8               ()V
  #10 = Utf8               <init>
  #11 = NameAndType        #10:#9         // "<init>":()V
  #12 = Methodref          #2.#11         // HelloWorldInScala$."<init>":()V
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               scala/Predef$
  #16 = Class              #15            // scala/Predef$
  #17 = Utf8               Lscala/Predef$;
  #18 = NameAndType        #6:#17         // MODULE$:Lscala/Predef$;
  #19 = Fieldref           #16.#18        // scala/Predef$.MODULE$:Lscala/Predef$;
  #20 = Utf8               Hello World!
  #21 = String             #20            // Hello World!
  #22 = Utf8               genericWrapArray
  #23 = Utf8               (Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
  #24 = NameAndType        #22:#23        // genericWrapArray:(Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
  #25 = Methodref          #16.#24        // scala/Predef$.genericWrapArray:(Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
  #26 = Utf8               printf
  #27 = Utf8               (Ljava/lang/String;Lscala/collection/Seq;)V
  #28 = NameAndType        #26:#27        // printf:(Ljava/lang/String;Lscala/collection/Seq;)V
  #29 = Methodref          #16.#28        // scala/Predef$.printf:(Ljava/lang/String;Lscala/collection/Seq;)V
  #30 = Utf8               this
  #31 = Utf8               args
  #32 = Utf8               [Ljava/lang/String;
  #33 = Methodref          #4.#11         // java/lang/Object."<init>":()V
  #34 = NameAndType        #6:#7          // MODULE$:LHelloWorldInScala$;
  #35 = Fieldref           #2.#34         // HelloWorldInScala$.MODULE$:LHelloWorldInScala$;
  #36 = Utf8               
  #37 = Class              #36            // 
  #38 = Utf8               $line4/$read
  #39 = Class              #38            // $line4/$read
  #40 = Utf8               
  #41 = Utf8               
  #42 = Class              #41            // 
  #43 = Utf8               HelloWorldInScala$
  #44 = Utf8               Code
  #45 = Utf8               LocalVariableTable
  #46 = Utf8               LineNumberTable
  #47 = Utf8               SourceFile
  #48 = Utf8               InnerClasses
  #49 = Utf8               Scala
{
  public static final HelloWorldInScala$ MODULE$;
    descriptor: LHelloWorldInScala$;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public static {};
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: new           #2                  // class HelloWorldInScala$
         3: invokespecial #12                 // Method "<init>":()V
         6: return

  public void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=2, args_size=2
         0: getstatic     #19                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
         3: ldc           #21                 // String Hello World!
         5: getstatic     #19                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
         8: iconst_0
         9: anewarray     #4                  // class java/lang/Object
        12: invokevirtual #25                 // Method scala/Predef$.genericWrapArray:(Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
        15: invokevirtual #29                 // Method scala/Predef$.printf:(Ljava/lang/String;Lscala/collection/Seq;)V
        18: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      19     0  this   LHelloWorldInScala$;
            0      19     1  args   [Ljava/lang/String;
      LineNumberTable:
        line 9: 0

  public HelloWorldInScala$();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #33                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: putstatic     #35                 // Field MODULE$:LHelloWorldInScala$;
         8: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   LHelloWorldInScala$;
      LineNumberTable:
        line 15: 0
}
SourceFile: "<console>"
InnerClasses:
     public static #40= #37 of #39; //=class  of class $line4/$read
     public static #40= #42 of #37; //=class  of class 
     public static #43= #2 of #42; //HelloWorldInScala$=class HelloWorldInScala$ of class 
Error: unknown attribute
  Scala: length = 0x0

デフォルトでは-vした結果を出力するようです。さて、JavaのHelloWorldと全然違う出力となりました。objectを使っている影響もあります。

HelloWorldInScala$という内部クラスを作っているようです。さすがにjavapで処理の流れで追うのはしんどくなってきました。一度scalacしてクラスファイルを生成し、そのクラスファイルをデコンパイルしてJavaコードにしてみます。デコンパイルはJD(http://jd.benow.ca/)を使いました。

import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001\025:Q!\001\002\t\002\025\t\021\003S3mY><vN\0357e\023:\0346-\0317b\025\005\031\021a\002\037f[B$\030PP\002\001!\t1q!D\001\003\r\025A!\001#\001\n\005EAU\r\0347p/>\024H\016Z%o'\016\fG.Y\n\003\017)\001\"a\003\b\016\0031Q\021!D\001\006g\016\fG.Y\005\003\0371\021a!\0218z%\0264\007\"B\t\b\t\003\021\022A\002\037j]&$h\bF\001\006\021\025!r\001\"\001\026\003\021i\027-\0338\025\005YI\002CA\006\030\023\tABB\001\003V]&$\b\"\002\016\024\001\004Y\022\001B1sON\0042a\003\017\037\023\tiBBA\003BeJ\f\027\020\005\002 E9\0211\002I\005\003C1\ta\001\025:fI\0264\027BA\022%\005\031\031FO]5oO*\021\021\005\004")
public final class HelloWorldInScala
{
  public static void main(String[] paramArrayOfString)
  {
    HelloWorldInScala..MODULE$.main(paramArrayOfString);
  }
}

import scala.Predef.;

public final class HelloWorldInScala$
{
  public static final  MODULE$;
  
  static
  {
    new ();
  }
  
  public void main(String[] args)
  {
    Predef..MODULE$.printf("Hello World!", Predef..MODULE$.genericWrapArray(new Object[0]));
  }
  
  private HelloWorldInScala$()
  {
    MODULE$ = this;
  }
}

内部クラスHelloWorldInScala$では、staticイニシャライザで自分をnewして、static final定数に代入しているようです。メインクラスHelloWorldInScalaのmain()メソッド内部クラスHelloWorldInScala$のインスタンスのmain()というメソッドを呼び出します。内部クラスのmain()メソッドではscala.Predef#printf()メソッドを呼び出すので"Hello World!"が出力されるというわけです。

まとめ

Java以外のJVM言語でjavapするとおもしろいよ!