50
InvokeDynamic @ytoshima

Invokedynamic / JSR-292

Embed Size (px)

Citation preview

Page 1: Invokedynamic / JSR-292

InvokeDynamic

@ytoshima

Page 2: Invokedynamic / JSR-292

概要• invokedynamic とは?

• Java7 で追加された invokedynamic バイトコード。静的型付け言語である Java 用の VM 上で動的言語が実装しやすくなる機能拡張。

• 利点は?• JVM 用の言語、ツール開発者にとっては便利な部品が増え、実装が行いやすくなったり、パフォーマンスの向上が見込まれます。

• 習得方法は?• 従来の Java platform 上にはなかった動作が導入されたため、動きを細かく確認しながら使用してみて、慣れる必要がある。後半で例を紹介します。

2

Page 3: Invokedynamic / JSR-292

Program Agenda

• JVM to Multi-language VM• Invoke bytecodes• InvokeDynamic• JSR-292 API• Examples

3

Page 4: Invokedynamic / JSR-292

Program Agenda

• JVM to Multi-language VM• Invoke bytecodes• InvokeDynamic• JSR-292 API• Examples

4

Page 5: Invokedynamic / JSR-292

JVM to Multi-Language VM

• Programming languages for Java Virtual Machine• http://www.is-research.de/info/vmlanguages/category/jvm-language/

Many languages are already available on JVM

5

BeanShell, Clojure, Groovy, JRuby, Mirah, JavaFX, Rhino, Nashorn, Kawa, Smalltalk,

Jython, Scala, Ceylon, Kotlin,... •動的言語、静的言語、それぞれ利点があり、実サービスでの利用も現れ、重要性が増している

Page 6: Invokedynamic / JSR-292

JVM to Multi-Language VM

• a multi-language renaissance for the JVM• http://openjdk.java.net/projects/mlvm/• mailing list http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev• sub-projects

The Da Vinci Machine Project

Dynamic Invocation Lightweight method objects

Lightweight bytecode loading Interface Injection

Continuations and stack introspection Tail calls and tail recursion

Tuples and value-oriented types Immediate wrapper types

Runtime support for closures Runtime support for multimethods

Faster interface invocation Faster reflectionSymbolic freedom

6

Page 7: Invokedynamic / JSR-292

JVM to Multi-Language VM

• Dynamic Invocation• 従来の命令と異なり少ない型情報で呼び出し可能な invokedynamic 命令を VM に追加。実行される際の振る舞いを1回目の実行前に呼び出される bootstrap メソッドでカスタマイズ可能にした。

• Lightweight method objects• 軽量のメソッド参照 MethodHandle とそれをサポートするクラス、

invokedynamic の bootstrap メソッドの戻り値である CallSite とその関連クラスを追加。MethodHandle は function pointer の様な位置づけ。

JSR-292

7

Page 8: Invokedynamic / JSR-292

JVM to Multi-Language VM

• 言語デザイナー、コンパイラーライター、ツール作成者,実行環境エンジニア,VM アーキテクト向けのオープンな技術会議

JVM Language Summit

8

• http://openjdk.java.net/projects/mlvm/jvmlangsummit/

• 下記 Wiki page より今年の資料が参照可能。半数近くが invokedynamic 関連

• http://www.wiki.jvmlangsummit.com/Main_Page

• 2011 のビデオが視聴可能

• http://medianetwork.oracle.com/media/show/16998 - 17017

• 17014 Method Handles and Beyond / John Rose

• 17008 JSR 292 Cookbook / Rémi Forax

• 17016 Adding invokedynamic Support to JRuby / Charles Nutter

Page 9: Invokedynamic / JSR-292

JVM to Multi-Language VM

• JSR-292 Cookbook• http://code.google.com/p/jsr292-cookbook/• JSR-292 API を使用したよくあるパターンのソースを公開

• https://github.com/headius/invokebinder• MethodHandle の連結を扱いやすくするライブラリ,開発中

Other resources

9

• サンプルコード• https://github.com/ytoshima/indy-samples• git clone https://github.com/ytoshima/indy-samples.git

Page 10: Invokedynamic / JSR-292

JVM to Multi-Language VM

• JRuby • invokedynamic を適用できる部分から使用し始めている。git clone http://github.com/jruby/jruby.git

Other resources

10

export JAVA_HOME=<path to jdk7>

cd jruby

ant

bin/jruby --bytecode test/fib.rb :

ALOAD 1

INVOKEDYNAMIC getFixnum (Lorg/jruby/runtime/ThreadContext;)Lorg/jruby/RubyFixnum; [org/jruby/runtime/invokedynamic/InvokeDynamicSupport.getFixnumBootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;J)Ljava/lang/invoke/CallSite; (6), 0]

SeeInvokeDynamicSupport.java

for bootstrap method implementation

Page 11: Invokedynamic / JSR-292

Program Agenda

• JVM to Multi-language VM• Invoke bytecodes• InvokeDynamic• JSR-292 API• Examples

11

Page 12: Invokedynamic / JSR-292

Invoke bytecodesJava6 で利用できる invoke* 命令

opcode description182 0xb6 invokevirtual インスタンスメソッドの呼び出し、クラスに応じてディスパッチ

183 0xb7 invokespecial コンストラクタ,private, super.* 呼び出し

184 0xb8 invokestatic スタティックメソッドの呼び出し

185 0xb9 invokeinterface インターフェイスによるメソッド呼び出し

186 0xba xxxunusedxxx 未使用 -> invokedynamic from java7

187 0xbb new オブジェクトの割当

12

すべて、呼び出し時にクラスの型情報が必要

http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html

Page 13: Invokedynamic / JSR-292

Invoke bytecodes

public static void main(String[] args) {System.out.println("hello");}

$ javap -c <class>

...

0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;

3: ldc #6; //String hello

5: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

invokevirtual の例。メソッド呼び出し時に receiver (または this)の型(ここでは PrintStream)は確定している。実際の receiver により method の virtual table を呼び出す動作は発生する。

動的言語では型の解決は実行時まで遅らされる事が多い。invokedynamic は型が確定していなくても呼び出しができる仕組みを提供する。

invokevirtual -- javap -private -verbose output

13

Page 14: Invokedynamic / JSR-292

Program Agenda

• JVM to Multi-language VM• Invoke bytecodes• InvokeDynamic• JSR-292 API• Examples

14

Page 15: Invokedynamic / JSR-292

InvokeDynamicinvokedynamic, MethodHandle 概要

15

• invokedynamic bytecode の一回目の実行で boot strap method というフックのような物が呼び出されて、その中で CallSite をその命令に関連づける

• CallSite には実際のメソッドを指す target という MethodHandle 型のフィールドがある。

• MutableCallSite やそのサブクラスは target を後で変更できる。• MethodHandle は引数を部分的に bind (部分適用?)したり,ダミーの引数を加えたりできる。

• 複数の MethodHandle を組み合わせたり,条件により呼び出す先を振り分ける事ができる。

Page 16: Invokedynamic / JSR-292

InvokeDynamic

• invokedynamic 命令を出力する java 文法はないため,バイトコード操作ライブラリ,例えば ASM 等を用いて生成する。

• http://asm.ow2.org/  asm4 がリリースされています。サンプルの *-asm4 が対応したサンプルです。

• http://forge.ow2.org/projects/asm/

invokedynamic

16

...invokedynamic (unlinked)...

1度目の実行...invokedynamic (linked)...

CallSite 作成CallSite の target 設定CallSite を return

CallSite target MethodHandle

Bootstrap method

Page 17: Invokedynamic / JSR-292

InvokeDynamicinvokedynamic

17

BytecodesDynamic callsites

Methodhandles

JVMJIT

Bytecodes は compiler あるいは dynamic runtimesで作られる

JVM はシームレスに実行を統合し必要に応じてネイティブコードに変換

ダイナミックコールサイトは各 invokedynamic バイトコードに作られる

各コールサイトは MethodHandleに関連づけられ,MethodHandle は bytecode のメソッド (Java のメソッドを指す)

Page 18: Invokedynamic / JSR-292

InvokeDynamic

invokedynamic 命令を含むクラスの javap による出力

         0: invokedynamic #22,  0   // InvokeDynamic #0:_:()Ljava/math

/BigDecimal;         5: areturn

// 関連する constant pool entry

  #21 = NameAndType        #20:#10        //  _:()Ljava/math/BigDecimal;

  #22 = InvokeDynamic      #0:#21         //  #0:_:()Ljava/math/BigDecimal;

BootstrapMethods:

0: #17 invokestatic DynamicIndyTest.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;)Ljava/lang/invoke/CallSite;

Method arguments: #19 1234567890.1234567890

invokedynamic -- javap -private -verbose output

18

ブートストラップメソッドのシグネチャ

bsm index : name : typename, type は bsm に渡される

Page 19: Invokedynamic / JSR-292

InvokeDynamic

import org.objectweb.asm.MethodVisitor;:Class<?> bsmClass = ... // bootstrap method を持つクラスString bsmName = “bsm” // bootstrap method の名前MethodType bsmType = ... //MethodVisitor mv = ...

// invokedynamic 命令の生成mv.visitInvokeDynamicInsn(name /* passed to bsm */, descriptor, /* target method type as string */ new Handle(H_INVOKESTATIC, bsmClass.getName().replace('.', '/'), bsmName, bsmType.toMethodDescriptorString()), bsmArgs);

ASM 4.0 での invokedynamic 生成概要

19

最終的に Byte[] のクラスデータを生成。これをファイルに落とせば javap 可能

Link to a Sample

Page 20: Invokedynamic / JSR-292

InvokeDynamicASM 4.0 での invokedynamic 生成概要

20

• ClassWriter で Gen + <N> という名前のクラスを作成• MethodVisitor で空のコンストラクタ <init> を作成• MethodVisitor で invokedynamic という static method を作成• MethodType を作成中のメソッドに設定• MethodVisitor の visitInvokeDynamicInsn メソッドで invokedynamic 命令を生成

• visitInvokeDynamicInsn の最後の引数は Object... bsmArgs となっていますが,Integer, Float, Long, Double, String, Type, Handle のいずれかのオブジェクトでなければなりません。

Page 21: Invokedynamic / JSR-292

Program Agenda

• JVM to Multi-language VM• Invoke bytecodes• InvokeDynamic• JSR-292 API• Examples

21

Page 22: Invokedynamic / JSR-292

JSR-292 APIPackage java.lang.invoke http://download.oracle.com/javase/7/docs/api/

22

クラス 記述

CallSite MethodHandle を target にもつホルダーのクラス。

ConstantCallSite target 不変の CallSite のサブクラス

MethodHandle 型を持ち、呼び出し可能なメソッド、フィールド等への参照。引数、戻り値の変形も可能。

MethodHandleProxies MethodHandle を interface 等に関連づけるためのクラス

MethodHandles MethodHandle に対する様々な変形等の操作を行う static method を持つクラス

MethodHandles.Lookup MethodHandle を探して返す factory object

MethodType 引数と戻り値の型を表す。MethodHandle の型、その呼び出し時の型

MutableCallSite target を変更可能な CallSite

SwitchPoint state の変更により、呼び出す MH をかえるオブジェクト

VolatileCallSite target が可変で volatile な CallSite

Page 23: Invokedynamic / JSR-292

JSR-292 API

23

Page 24: Invokedynamic / JSR-292

JSR-292 API

// Lookup のインスタンスを取得MethodType mt; MethodHandle mh;MethodHandles.Lookup lookup = MethodHandles.lookup();

// String.class の public String replace(char, char) の// MethodType を作成 mt is (char,char)Stringmt = MethodType.methodType(/* rtype */ String.class, /* ptype0 */ char.class, /* ptype1 */ char.class);

// メソッドを探し,MethodHandle に代入mh = lookup.findVirtual(/* refc */String.class, "replace", mt);

// 呼び出し、インスタンスメソッドのため,最初の引数は receivers = (String) mh.invokeExact("daddy",'d','n');

// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;// -> “nanny”, same as: “daddy”.replace(‘d’,’n’)

シンプルな使用例

24

Page 25: Invokedynamic / JSR-292

JSR-292 API

• MethodHandles の nested class

• findVirtual, findStatic メソッドでクラス、メソッド名、メソッドタイプからメソッドを見つけ、MethodHandle として返す。

• MethodHandles.lookup() • 呼び出されるクラスですべてのアクセス権をもつ Lookup object を返す

• MethodHandles.publicLookup() • public のみを lookup できる Lookup object を返す

• bootstramp method の lookup 引数• 呼び出し元の invokedynamic 命令を含むメソッドのクラスからみた、すべてのアク

セス権を持つ Lookup オブジェクトへの参照。• lookupClass() でどのクラスに関する物か分かる• lookupModes() どのアクセス権が許されているかが分かる(PUBLIC, PROTECTED, ...)

MethodHandles.Lookup

25

Page 26: Invokedynamic / JSR-292

JSR-292 API

import java.lang.invoke.*;public class BindEx { public static void main(String[] main) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle mh_replace = lookup.findVirtual(String.class, "replace", MethodType.methodType(String.class, char.class, char.class)); D((String)mh_replace.invokeExact("daddy", 'd', 'n') + " // " + mh_replace.type()); D(mh_replace.invoke("daddy", 'd', 'n')); MethodHandle mh_bound = mh_replace.bindTo("daddy"); D("mh_bound: " + (String)mh_bound.invokeExact('d', 'n') + " // " + mh_bound.type()); mh_bound = MethodHandles.insertArguments(mh_replace, 0, "daddy"); D("mh_bound: " + (String)mh_bound.invokeExact('d', 'n') + " // " + mh_bound.type()); MethodHandle mh_bound_all = MethodHandles.insertArguments(mh_bound, 0, 'd', 'n'); D((String)mh_bound_all.invokeExact() + " // " + mh_bound_all.type()); } public static void D(Object o) { System.out.println("D: " + o); }}

insertArguments, bindTo

26

Page 27: Invokedynamic / JSR-292

JSR-292 APIinsertArguments, bindTo

27

mh_replace(String,char,char)String

bindTo 最初の引数として “daddy” をbind

mh_bound(char,char)String

mh_bound_all()String

insertArguments

‘d’, ‘n’ も引数に設定 output:D: nanny // (String,char,char)StringD: nannyD: mh_bound: nanny // (char,char)StringD: mh_bound: nanny // (char,char)StringD: nanny // ()String

作成時の流れ呼び出しときの流れ

MH 作成時に覚えさせておきたい引数を指定しておける。fallback に CallSiteを覚えさせれば target 再設定に使用できる。

Page 28: Invokedynamic / JSR-292

JSR-292 API

import java.lang.invoke.*;import java.util.*;public class DropEx { public static void main(String[] main) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle mh_replace = lookup.findVirtual(String.class, "replace", MethodType.methodType(String.class, char.class, char.class)); MethodHandle mh_drop = MethodHandles.dropArguments(mh_replace, 1, Map.class, Set.class); D("mh_drop: " + (String)mh_drop.invokeExact("daddy", (Map)new HashMap(), (Set)new TreeSet(), 'd', 'n') + " // " + mh_drop.type()); D("mh_drop: " + (String)mh_drop.invoke("daddy", new HashMap(), new TreeSet(), 'd', 'n') + " // " + mh_drop.type()); } public static void D(Object o) { System.out.println("D: " + o);}}

dropArguments: ダミー引数を追加

28

引数の数、型を合わせる場合に使用できる

Page 29: Invokedynamic / JSR-292

JSR-292 APIdropArguments: ダミー引数を追加

29

mh_replace(String,char,char)String

mh_drop(String,Map,Set,char,char)String

dropArguments

ダミー引数(呼び出し時には捨てられる)を追加。シグネチャを合わせる等

output:D: mh_drop: nanny // (String,Map,Set,char,char)StringD: mh_drop: nanny // (String,Map,Set,char,char)String

作成時の流れ呼び出しときの流れ

Page 30: Invokedynamic / JSR-292

JSR-292 API

public class AsCollectorEx { public static void main(String[] args) throws Throwable { Lookup lookup = lookup(); MethodHandle m0 = lookup.findStatic(AsCollectorEx.class, "printAll", methodType(void.class, Object[].class)); P("m0 : " + m0); MethodHandle mcoll = m0.asCollector(Object[].class, 4); mcoll.invoke("Foo", "Bar", "Fizz", "Buzz"); mcoll = mcoll.asType(methodType(void.class, String.class, String.class, String.class, String.class)); P("mcoll: " + mcoll); mcoll.invokeExact("Foo", "Bar", "Fizz", "Buzz"); Object[] params = new Object[] {"Foo", "Bar", "Fizz", "Buzz"}; MethodHandle mspr = mcoll.asSpreader(Object[].class, 4); P("mspr: " + mspr); mspr.invokeExact(params); } public static void printAll(Object[] args) { ... }

asType, asCollector: 配列、引数リスト変換

30

Page 31: Invokedynamic / JSR-292

JSR-292 APIasType, asCollector: 配列、引数リスト変換

31

m0(Object[])void

mcoll 1(Object,Object,Object,Object)void

mcoll 2(String,String,String,String)void

mspr(Object[])void

asCollector

asType

asSpreader

output:m0 : MethodHandle(Object[])voidmcoll 1: MethodHandle(Object,Object,Object,Object)void(Foo Bar Fizz Buzz )mcoll 2: MethodHandle(String,String,String,String)void(Foo Bar Fizz Buzz )mspr: MethodHandle(Object[])void(Foo Bar Fizz Buzz )

Page 32: Invokedynamic / JSR-292

JSR-292 API

public class FoldEx { public static void main(String[] main) throws Throwable { Lookup lookup = lookup(); MethodHandle mh_replace = lookup.findVirtual(String.class, "replace", methodType(String.class, char.class, char.class)); MethodHandle mh_hdr = lookup.findStatic(FoldEx.class, "printHeader", methodType(String.class, char.class, char.class)); MethodHandle mh_folded = foldArguments(mh_replace, mh_hdr); D("mh_folded type: " + mh_folded.type()); D("mh_fold call: " + mh_folded.invoke('d', 'n')); } public static void D(Object o) { System.out.println("D: " + o);} public static String printHeader(char fromC, char toC) { System.out.println("* params: " + fromC + ", " + toC); return "daddy"; }

foldArguments: 引数の前処理

32

Page 33: Invokedynamic / JSR-292

JSR-292 APIfoldArguments: 引数の前処理

33

target: mh_replace(String, char, char)String

combiner: mh_hdr(String, char, char)String

mh_folded(char,char)String

foldArgumentscombiner の戻り値が void でない場合,target 呼び出し前に引数を先頭に加える

JSR-292 Cookbook の Interceptors では foldArguments を使用して Interceptor 機能を実装している。(メソッドの前、後に他のメソッドを呼ばせるなど)

作成時の流れ呼び出しときの流れ

Page 34: Invokedynamic / JSR-292

JSR-292 API

public class FilterReturnEx { public static void main(String[] main) throws Throwable { Lookup lookup = lookup(); MethodHandle mh_d2s = lookup.findStatic(FilterReturnEx.class, "double2String", methodType(String.class, double.class)); MethodHandle mh_s2bi = lookup.findStatic(FilterReturnEx.class, "string2BigDecimal", methodType(BigDecimal.class, String.class)); MethodHandle mh_filtered = filterReturnValue(mh_d2s, mh_s2bi); D("mh_filtered type: " + mh_filtered.type()); D("mh_filtered call: " + (BigDecimal)mh_filtered.invokeExact(123.456)); } public static String double2String(double dval) { return Double.toString(dval); } public static BigDecimal string2BigDecimal(String str) { return new BigDecimal(str); }

filterReturnValue: 戻り値の変換

34

Page 35: Invokedynamic / JSR-292

JSR-292 APIfilterReturnValue: 戻り値の変換

35

target: mh_d2s(double)String

rv filter: mh_s2bi(String)BigDecimal

mh_filtered(double)BigDecimal

filterReturnValuetarget の返した String オブジェクトをBigDecimal に filter で変換している

作成時の流れ呼び出しときの流れ

Page 36: Invokedynamic / JSR-292

JSR-292 API

public class FilterEx { public static void main(String[] main) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle mh_domatch = lookup.findStatic(FilterEx.class, "domatch", MethodType.methodType(Matcher.class, String.class, Pattern.class)); MethodHandle mh_getPtn = lookup.findStatic(FilterEx.class, "getPtn", MethodType.methodType(Pattern.class, String.class)); MethodHandle mh_filtered = MethodHandles.filterArguments(mh_domatch, 1, mh_getPtn); D("mh_filtered type: " + mh_filtered.type()); D("mh_filtered call: " + mh_filtered.invoke("daddy", ".*dd.*")); } public static Matcher domatch(String str, Pattern ptn) { Matcher matcher = ptn.matcher(str); if (matcher.matches()) { System.out.println("matched: str: " + str + " ptn: " + ptn); } else { System.out.println("Did not match: str: " + str + " ptn: " + ptn); } return matcher; } public static Pattern getPtn(String sptn) { return Pattern.compile(sptn); }

filterArguments: 引数の変換

36

Page 37: Invokedynamic / JSR-292

JSR-292 APIfilterArguments: 引数の変換

37

output:D: mh_filtered type: (String,String)Matchermatched: str: daddy ptn: .*dd.*D: mh_filtered call: java.util.regex.Matcher[pattern=.*dd.* region=0,5 lastmatch=daddy]

target: mh_domatch(String,Pattern)Matcher

filters: mh_getPtn [...](String)Pattern

mh_filtered(String,String)Matcher

filterArguments

フィルターを指定した引数にかけて変換してから target を呼び出す

作成時の流れ呼び出しときの流れ

Page 38: Invokedynamic / JSR-292

JSR-292 API

public class GWTEx { public static void main(String[] args) throws Throwable { Lookup lookup = lookup(); MethodHandle test = lookup.findStatic(GWTEx.class, "isString", methodType(boolean.class, Object.class)); MethodHandle target = lookup.findStatic(GWTEx.class, "printString", methodType(void.class, Object.class, long.class)); MethodHandle fallback = lookup.findStatic(GWTEx.class, "printObject", methodType(void.class, Object.class, long.class, long.class)); MethodHandle test1 = dropArguments(test, 1, long.class); MethodHandle fallback1 = insertArguments(fallback, 2, 99); MethodHandle mh = guardWithTest(test1, target, fallback1); mh.invoke("This is a String", -1L); mh.invoke(new Integer(10), -1L); D("test : " + test); D("test1 : " + test1); D("target : " + target); D("fallback : " + fallback); D("fallback1: " + fallback1); D("mh : " + mh); } static boolean isString(Object o) { return (o instanceof String); } static void printString(Object o, long t) { System.out.println("printString: " + (String)o + " t: " + t); } static void printObject(Object o, long t, long t2) { System.out.println("printObject: " + o + " t: " + t + " t2: " + t2); }

guardWithTest: テスト結果による振り分け

38

Page 39: Invokedynamic / JSR-292

JSR-292 APIguardWithTest: テスト結果による振り分け

39

test(Object)boolean

target(Object,long)void

test1(Object,long)boolean

fallback(Object,long,long)void

fallback1(Object,long)void

mh(Object,long)void

dropArgumentsinsertArguments

guardWithTest

output:printString: This is a String t: -1printObject: 10 t: -1 t2: 99:

true

falsefallback 側に CallSite を渡し、false の場合にCallSite を書き換えるという使い方ができる

作成時の流れ呼び出しときの流れ

Page 40: Invokedynamic / JSR-292

Program Agenda

• JVM to Multi-language VM• Invoke bytecodes• InvokeDynamic• JSR-292 API• Examples

40

Page 41: Invokedynamic / JSR-292

Examples

• JSR-292 Cookbook の inlining-cache サンプルを単純化• guardWithTest で test, target, fallback を連結。メソッドを呼びだす操作の前に,receiver のクラスを test に覚えさせておく。true 時に呼び出すメソッドをtarget に、false の場合には一連の再設定を行う fallback を呼び出す様にする。

• 次回呼び出しで覚えておいた前回のクラスと呼び出しのオブジェクトのクラスが一致したらキャッシュしておいた target を呼び出す。

Inline Cache Link to the sample

41

Page 42: Invokedynamic / JSR-292

Examples

DynamicIndy dynamicIndy = new DynamicIndy(); MethodHandle mh = dynamicIndy.invokeDynamic("talk", MethodType.methodType(void.class, Object.class), Pet.class, "bsm", MethodType.methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object.class), "dummy");

Object[] pets = new Object[] {new Cat(), new Dog(), new Cat(), new Dog()}; for (Object pet : pets) { for (int i = 0; i < 4; i++) { mh.invokeExact(pet); } }

invokedynamic の生成等 (Pet.java main)

42

Page 43: Invokedynamic / JSR-292

Examples

public static CallSite bsm(Lookup lookup, String name, MethodType type, Object arg) throws Throwable { D("bsm: type: " + type); InlineCacheCallSite site = new InlineCacheCallSite(lookup, name, type); MethodHandle fallback = FALLBACK_MH.bindTo(site); fallback = fallback.asCollector(Object[].class, type.parameterCount()); D("bsm: fallback: " + fallback); fallback = fallback.asType(type); D("bsm: fallback: " + fallback); site.setTarget(fallback); return site; }// lookup, name, type を保持する MutableCallSite のサブクラスのオブジェクトを// 作り、fallback にそれを bind し、CallSite の target を引数を加工した// fallback にして、CallSite をリターン

bootstrap method

43

Page 44: Invokedynamic / JSR-292

Examples

public static void fallback(InlineCacheCallSite site, Object[] args) throws Throwable { MethodType type = site.type(); Object receiver = args[0]; Class<?> receiverClass = receiver.getClass(); MethodHandle target = site.lookup.findVirtual(receiverClass, site.name, type.dropParameterTypes(0, 1)); target = target.asType(site.getTarget().type()); MethodHandle test = CHECK_CLASS_MH.bindTo(receiverClass); test = test.asType(test.type().changeParameterType(0, type.parameterType(0)));

MethodHandle fallback = FALLBACK_MH.bindTo(site); fallback = fallback.asCollector(Object[].class, type.parameterCount()); fallback = fallback.asType(type);

MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback); site.setTarget(guard); target.invokeWithArguments(args); }

fallback method

44

Page 45: Invokedynamic / JSR-292

Examples

public static boolean isCachedClass(Class<?> cls, Object receiver) { if (cls != receiver.getClass()) D("isCachedClass false !"); else D("isCachedClass true."); return cls == receiver.getClass(); }

test method

45

Page 46: Invokedynamic / JSR-292

ExamplesInline Cache

46

invokedynamic 命令の生成

invokedynamic 命令の実行

bootstrap method 起動

CallSite を fallback に設定して return

fallback 呼び出しtest MH の生成 class 値の登録、target 決定testWithGuard で test, target, fallback を連結call site 再設定、target

呼び出し

invokedynamic 命令の実行

test で arg0 のクラスがキャッシュと同じか確認

target の実行

Y

N

Page 47: Invokedynamic / JSR-292

Examples

• Inline の例と似た流れ。

• SwitchPoint guardWithTest で target, fallback を連結。SwitchPoint が valid の間は target に設定された値が返り、invalidate されたら fallback で再設定。

• SwitchPoint は別な箇所から invalidate される、そのため、SwitchPoint のインスタンスは invalidate する側が管理する形にしている。

SwitchPoint example link to the sample

47

Page 48: Invokedynamic / JSR-292

Examples

public static CallSite bsm(Lookup lookup, String name, MethodType type, Object arg) throws Throwable { AmethystConstantCallSite site = new AmethystConstantCallSite(type, "site1"); MethodType fallbackType = type.insertParameterTypes(0, AmethystConstantCallSite.class); MethodHandle myFallback = insertArguments( lookup.findStatic(SwitchPointEx.class, "constantFallback",fallbackType), 0, site); site.setTarget(myFallback);

return site; }

bootstrap method

48

Page 49: Invokedynamic / JSR-292

Examples

public static IAmethystObject constantFallback(AmethystConstantCallSite site, MyThreadContext context) { IAmethystObject value = context.getConstant(site.name()); if (value != null) { MethodHandle valueHandle = constant(IAmethystObject.class, value); valueHandle = dropArguments(valueHandle, 0, MyThreadContext.class); MethodHandle fallback = insertArguments( findStatic(SwitchPointEx.class, "constantFallback", methodType(IAmethystObject.class, AmethystConstantCallSite.class, MyThreadContext.class)), 0, site); SwitchPoint switchPoint = (SwitchPoint)context.runtime.getConstantInvalidator().getData(); MethodHandle gwt = switchPoint.guardWithTest(valueHandle, fallback); site.setTarget(gwt); } else { D("In constantFallback value=null"); value = new IAmethystObject("(null)"); } return value; }

fallback method

49

Page 50: Invokedynamic / JSR-292

効果、まとめ

• パフォーマンスは数% から10倍程度の向上が紹介されているが、処理に依存する部分もある。invokedynamic/JSR-292 API 関連のパフォーマンス向上の余地もある。

• 静的型付け言語の java のメソッド呼び出しにとらわれないメソッド呼び出し方法が提供された事で動的言語の実装改良の他にも様々な応用の可能性が考えられる。

• Java 8 の Closure は invokedynamic, MethodHandle を基にする可能性が高い。

50