Java etc...

アクセスカウンタ

zoom RSS Lambda が indy に出会ったら ...

<<   作成日時 : 2013/02/26 23:55   >>

ブログ気持玉 0 / トラックバック 0 / コメント 0

(JDK 8 EA with Lambda Support ではなく)JDK8 EA b77でラムダ式が扱えるみたいなので、Lambda が invokedynamic を生成するところを見てみた。

ちなみに、b78(Develpoer Preview になるのか ?!) のバイナリスナップショットの公開がまだなのと、b78 のソース(はおそらく参照できると思うのだけど、そこ)からビルドするのは面倒なので、b77 で試した。ただし、API の対応がまだなので、以前のコードはコンパイルできないので、Runnable を想定したラムダ式で試す。

ソースコードは、こんな感じ。

import java.util.*;

public class InvokeTest5 {
 List<String> list = new ArrayList<String>();

 { list.add("Hello,"); }

 static private void newLine() { System.out.println(); }

 private void makeList(String[] sa) {
  list.addAll(Arrays.asList(sa));
 }

 public void go1(String[] sa) {
  makeList(sa);
  new Thread(
    () -> {
      for(String s : list)
        System.out.print(s + " ");
      newLine();
    }
  ).start();
 }

 public void go2(String[] sa) {
  makeList(sa);
  for(String s : list) {
    new Thread(
      () -> System.out.print(s + " ")
    ).start();
  }
  newLine();
 }

 public static void main(String args[]) {
  new InvokeTest5().go1(args);
  new InvokeTest5().go2(args);
 }
}


javap すると、((-c -p -l -v -s -sysinfo -constants オプション指定した結果から一部の情報を割愛しています)

Classfile InvokeTest5.class
public class InvokeTest5
 SourceFile: "InvokeTest5.java"
 InnerClasses:
  public static final #137= #136 of #142;
    // Lookup=class java/lang/invoke/MethodHandles$Lookup of
    //     class java/lang/invoke/MethodHandles


invokedynamic から呼び出される bootstrap method は、どれも java.lang.invoke.LambdaMetafactory.metaFactory。

 BootstrapMethods:
  0: #70 invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(
            Ljava/lang/invoke/MethodHandles$Lookup;
            Ljava/lang/String;
            Ljava/lang/invoke/MethodType;
            Ljava/lang/invoke/MethodHandle;
            Ljava/lang/invoke/MethodHandle;
            Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
   Method arguments:
     #71 invokeinterface java/lang/Runnable.run:()V
     #72 invokevirtual InvokeTest5.lambda$0:()V
     #73 ()V

  1: #70 invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(
            Ljava/lang/invoke/MethodHandles$Lookup;
            Ljava/lang/String;
            Ljava/lang/invoke/MethodType;
            Ljava/lang/invoke/MethodHandle;
            Ljava/lang/invoke/MethodHandle;
            Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
   Method arguments:
     #71 invokeinterface java/lang/Runnable.run:()V
     #82 invokestatic InvokeTest5.lambda$1:(Ljava/lang/String;)V
     #73 ()V
  minor version: 0
  major version: 52


ちなみに、クラスファイルのバージョン番号は '52'。これは、 Java SE 8 のバージョン番号です。

go1() メソッド内での invokedynamic は、Invokedynamic #0:lambda:(LInvokeTest5;)Ljava/lang/Runnable; と #0 が指定されているので、上記の Bootstrapmethod の 0: が使われる。

 public void go1(java.lang.String[]);
  Signature: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC
  Code:
   stack=3, locals=2, args_size=2
     0: aload_0
     1: aload_1
     2: invokespecial #11    // Method makeList:([Ljava/lang/String;)V
     5: new           #12    // class java/lang/Thread
     8: dup
     9: aload_0
    10: invokedynamic #13, 0
            // InvokeDynamic #0:lambda:(LInvokeTest5;)Ljava/lang/Runnable;
    15: invokespecial #14
            // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
    18: invokevirtual #15
            // Method java/lang/Thread.start:()V
    21: return


go2() メソッド内での invokedynamic は、InvokeDynamic #1:lambda:(Ljava/lang/String;)Ljava/lang/Runnable; と #1 が指定されているので、上記の Bootstrapmethod の 1: が使われる。

 public void go2(java.lang.String[]);
  Signature: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC
  Code:
   stack=3, locals=4, args_size=2
      0: aload_0
      1: aload_1
      2: invokespecial   #11   // Method makeList:([Ljava/lang/String;)V
      5: aload_0
      6: getfield        #4    // Field list:Ljava/util/List;
      9: invokeinterface #16, 1
             // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
     14: astore_2
     15: aload_2
     16: invokeinterface #17, 1
             // InterfaceMethod java/util/Iterator.hasNext:()Z
     21: ifeq          53
     24: aload_2
     25: invokeinterface #18, 1
             // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     30: checkcast       #19      // class java/lang/String
     33: astore_3
     34: new             #12      // class java/lang/Thread
     37: dup
     38: aload_3
     39: invokedynamic   #20, 0
             // InvokeDynamic #1:lambda:(Ljava/lang/String;)Ljava/lang/Runnable;
     44: invokespecial   #14
             // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
     47: invokevirtual   #15      // Method java/lang/Thread.start:()V
     50: goto            15
     53: invokestatic    #21      // Method newLine:()V
     56: return


この metaFactory が生成する CallSite オブジェクトの target は、この呼び出しのあるクラスの内部クラスで、その内部クラスは、java.lang.invoke.MagicLambdaImpl を extends し、適切な関数型インタフェースを implements する(この場合の java.lang.Runnable や java.util.function パッケージのインタフェースなど)。
なお、この内部クラスは、コンパイル時には生成されず、実行時に動的に生成される。(通常は)外部ファイルに出力されない。

以下の javap の出力は、java.lang.invoke.InnerClassLambdaMetaFactory.java の private <T> Class<? extends T> spinInnerClass() メソッドの実装の一部にあるコメント(/*** Uncomment to dump the generated fileの箇所)をはずしコンパイルする。そして、実行時に -Xbootclasspath/p:"コンパイルしたクラスファイルの出力フォルダ" を指定して実行すると、クラスファイルが生成される。
その内部クラスに javap すると、以下のようになった。

Classfile InvokeTest5$$Lambda$1.class
class InvokeTest5$$Lambda$1 extends java.lang.invoke.MagicLambdaImpl implements java.lang.Runnable
 minor version: 0
 major version: 51
 flags: ACC_SUPER
{
 private final InvokeTest5 arg$1;
  Signature: LInvokeTest5;
  flags: ACC_PRIVATE, ACC_FINAL

 private InvokeTest5$$Lambda$1(InvokeTest5);
  Signature: (LInvokeTest5;)V
  flags: ACC_PRIVATE
  Code:
   stack=2, locals=2, args_size=2
     0: aload_0
     1: invokespecial #13 // Method java/lang/invoke/MagicLambdaImpl."<init>":()V
     4: aload_0
     5: aload_1
     6: putfield      #15 // Field arg$1:LInvokeTest5;
     9: return

 public void run();
  Signature: ()V
  flags: ACC_PUBLIC
  Code:
   stack=1, locals=1, args_size=1
     0: aload_0
     1: getfield      #15 // Field arg$1:LInvokeTest5;
     4: invokevirtual #21 // Method InvokeTest5.lambda$0:()V
     7: return
}


Classfile InvokeTest5$$Lambda$2.class
class InvokeTest5$$Lambda$2 extends java.lang.invoke.MagicLambdaImpl implements java.lang.Runnable
 minor version: 0
 major version: 51 flags: ACC_SUPER
{
 private final java.lang.String arg$1;
  Signature: Ljava/lang/String;
  flags: ACC_PRIVATE, ACC_FINAL

 private InvokeTest5$$Lambda$2(java.lang.String);
  Signature: (Ljava/lang/String;)V
  flags: ACC_PRIVATE
  Code:
   stack=2, locals=2, args_size=2
     0: aload_0
     1: invokespecial #13 // Method java/lang/invoke/MagicLambdaImpl."<init>":()V
     4: aload_0
     5: aload_1
     6: putfield      #15 // Field arg$1:Ljava/lang/String;
     9: return

 public void run();
  Signature: ()V
  flags: ACC_PUBLIC
  Code:
   stack=1, locals=1, args_size=1
     0: aload_0
     1: getfield      #15 // Field arg$1:Ljava/lang/String;
     4: invokestatic  #21 // Method InvokeTest5.lambda$1:(Ljava/lang/String;)V
     7: return
}


それぞれの内部クラスの単一メソッド(runメソッド)内は、ラムダ式の本体処理を実装したメソッド(それぞれ lambda$0, lambda$1 メソッド)を呼び出している。
lambda$0 はインスタンスメソッドとして invokevirtual を使って、lambda$1 は static メソッドとして invokestatic を使って、それぞれ呼び出されている。

これらのメソッドが、インスタンスメソッドになるか、static メソッドになるかは、Translation of Lambda Expressions の "static vs. instance methods" に記載がある。lambda$0() は、ラムダ式を囲んでいるオブジェクトの list フィールドを参照しているので private インスタンスメソッドに変換されるので、インスタンスメソッドとして呼び出される。lambda$1() は、ラムダ式を囲んでいるオブジェクト(this, super または、そのフィールド)を参照しないので、private static に変換されるので static メソッドとして呼び出される。

lambda$0, lambda$1 は、ラムダ式の本体処理を実装したメソッドであり、これらは、このラムダ式があるクラス(InvokeTest5)内のメソッドとして実装される

 private void lambda$0();
  Signature: ()V
  flags: ACC_PRIVATE, ACC_SYNTHETIC
  Code:
   stack=3, locals=3, args_size=1
     0: aload_0
     1: getfield        #4      // Field list:Ljava/util/List;
     4: invokeinterface #16, 1
             // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
     9: astore_1
    10: aload_1
    11: invokeinterface #17, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
    16: ifeq            57
    19: aload_1
    20: invokeinterface #18, 1
             // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
    25: checkcast       #19    // class java/lang/String
    28: astore_2
    29: getstatic       #7     // Field java/lang/System.out:Ljava/io/PrintStream;
    32: new             #26    // class java/lang/StringBuilder
    35: dup
    36: invokespecial   #27    // Method java/lang/StringBuilder."<init>":()V
    39: aload_2
    40: invokevirtual   #28
             // Method java/lang/StringBuilder.append:(Ljava/lang/String;)
             // Ljava/lang/StringBuilder;
    43: ldc             #29    // String
    45: invokevirtual   #28
             // Method java/lang/StringBuilder.append:(Ljava/lang/String;)
             // Ljava/lang/StringBuilder;
    48: invokevirtual   #30
             // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    51: invokevirtual   #31
             // Method java/io/PrintStream.print:(Ljava/lang/String;)V
    54: goto          10
    57: invokestatic    #21    // Method newLine:()V
    60: return


 private static void lambda$1(java.lang.String);
  Signature: (Ljava/lang/String;)V
  flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
  Code:
   stack=3, locals=1, args_size=1
     0: getstatic     #7  // Field java/lang/System.out:Ljava/io/PrintStream;
     3: new           #26 // class java/lang/StringBuilder
     6: dup
     7: invokespecial #27 // Method java/lang/StringBuilder."<init>":()V
    10: aload_0
    11: invokevirtual #28
             // Method java/lang/StringBuilder.append:(Ljava/lang/String;)
             // Ljava/lang/StringBuilder;
    14: ldc           #29 // String
    16: invokevirtual #28
             // Method java/lang/StringBuilder.append:(Ljava/lang/String;)
             // Ljava/lang/StringBuilder;
    19: invokevirtual #30
             // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    22: invokevirtual #31 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
    25: return


最後に "java InvokeTest5 a b c" と実行すると、実行の度に、出力される結果はさまざまになります。これは、2 つのスレッドが並行して動作するからです。

テーマ

注目テーマ 一覧


月別リンク

ブログ気持玉

クリックして気持ちを伝えよう!
ログインしてクリックすれば、自分のブログへのリンクが付きます。
→ログインへ

トラックバック(0件)

タイトル (本文) ブログ名/日時

トラックバック用URL help


自分のブログにトラックバック記事作成(会員用) help

タイトル
本 文

コメント(0件)

内 容 ニックネーム/日時

コメントする help

ニックネーム
本 文
Lambda が indy に出会ったら ... Java etc.../BIGLOBEウェブリブログ
文字サイズ:       閉じる