6.72. kink/javahost/METHOD_HANDLE¶
java.lang.MethodHandleを操作するユーティリティ関数を提供する。
6.72.1. METHOD_HANDLE.invoke(Mh Args ...[$config={}])¶
invokeは、メソッドハンドルMhを、MethodHandle.invokeWithArgumentsで呼び出す。Argsが引数列として渡される。
Java.call_method, .call_static, .newとは異なり、Argsの要素のtype属性は、パラメータ型を示す必要はない。たとえば、すべてjava.lang.Objectであっても構わない。これは、メソッドハンドル自体が型情報を持っているためである。
$configにはjava_call_config値が渡される。java_call_config型については、kink/javahost/JAVAを見よう。
結果
1) メソッドハンドルの戻り型が参照型rtであり、呼び出しがresultObjを戻したとき、invokeはjava値(resultObj, rt)を引数として成功時継続を末尾呼び出しする。
2) メソッドハンドルの戻り型ptがchar, boolean, byte, short, int, long, float, doubleのいずれかであり、呼び出しが値を戻したとき、invokeはjava値(box, pt)を引数として成功時継続を末尾呼び出しする。ここでboxは、戻り値のラッパインスタンスである。
3) メソッドハンドルの戻り型がvoidで、呼び出しが戻ったとき、invokeは成功時継続を引数なしで末尾呼び出しする。
4) 呼び出しがJavaの例外exceptionObjを投げて終わり、その例外がC.catchの呼び出しで指定されたひとつ以上の例外型のインスタンスであるとき、invokeは、最も狭い例外型etに対応するエラー時継続を、java値(exceptionObj, et)を引数として末尾呼び出しする。
5) 呼び出しがJavaの例外を投げて終わり、C.catchに渡された例外型のどれもその例外に合致しないとき、invokeはKinkの例外を投げる。
事前条件
Mhはjava値(mh, *)でなければならない。ここでmhはMethodHandleのインスタンスでなければならない。
Argsは、java値を要素とするvecでなければならない。それら要素のobjectReference属性は、mhの対応するパラメータ型で型付け可能でなければならない。
$configは、java_call_configを取る関数でなければならない。
6.72.2. METHOD_HANDLE.instance_method(Owner_class Method_name Param_classes Ret_class ...[$config={}])¶
instance_methodは、publicなインスタンスメソッドのメソッドハンドルを検索する。
コンフィグメソッド:
• C.on_success($success): default = VAL.identity
• C.on_no_such_method($no_such_method): default = 例外を投げる関数
• C.on_illegal_access($illegal_access): default = 例外を投げる関数
instance_methodは、 MethodHandles.publicLookup().findVirtual を使ってメソッドを検索する。ここでOwner_classはメソッドを持つクラス、Method_nameはメソッド名、Param_classesはパラメータ型の列、Ret_classは戻り値の型として使う。
結果
指定されたメソッドが見つかったとき、instance_methodは、java値(mh, MethodHandle)を引数として$successを末尾呼び出しする。ここでmhは、メソッドのメソッドハンドルである。メソッドハンドルのパラメータ型の列は `[Owner_class] + Param_classes`, 戻り型はRet_classになる。
NoSuchMethodExceptionの例外nsmeが投げられた場合、instance_methodは、java値(nsme, NoSuchMethodException)を引数として$no_such_methodを末尾呼び出しする。
IllegalAccessExceptionの例外iaeが投げられた場合、instance_methodは、java値(iae, IllegalAccessException)を引数として$illegal_accessを末尾呼び出しする。
事前条件
Owner_classは、java値(oc, *)でなければならない。ここでocは、java.lang.Classのインスタンスでなければならない。
Method_nameはstrでなければならない。
Param_classesは、java値(pc, *)のvecでなければならない。ここでpcは、java.lang.Classのインスタンスでなければならない。
Ret_classは、java値(rc, *)でなければならない。ここでrcは、java.lang.Classのインスタンスでなければならない。
6.72.3. METHOD_HANDLE.static_method(Owner_class Method_name Param_classes Ret_class ...[$config={}])¶
static_methodは、publicなstaticメソッドのメソッドハンドルを検索する。
コンフィグメソッド:
• C.on_success($success): default = VAL.identity
• C.on_no_such_method($no_such_method): default = 例外を投げる関数
• C.on_illegal_access($illegal_access): default = 例外を投げる関数
static_methodは、 MethodHandles.publicLookup().findStatic を使ってメソッドを検索する。ここでOwner_classはメソッドを持つクラス、Method_nameはメソッド名、Param_classesはパラメータ型の列、Ret_classは戻り値の型として使う。
結果
指定されたメソッドが見つかったとき、static_methodは、java値(mh, MethodHandle)を引数として$successを末尾呼び出しする。ここでmhは、メソッドのメソッドハンドルである。メソッドハンドルのパラメータ型の列はParam_classes, 戻り型はRet_classになる。
NoSuchMethodExceptionの例外nsmeが投げられた場合、instance_methodは、java値(nsme, NoSuchMethodException)を引数として$no_such_methodを末尾呼び出しする。
IllegalAccessExceptionの例外iaeが投げられた場合、instance_methodは、java値(iae, IllegalAccessException)を引数として$illegal_accessを末尾呼び出しする。
事前条件
Owner_classは、java値(oc, *)でなければならない。ここでocは、java.lang.Classのインスタンスでなければならない。
Method_nameはstrでなければならない。
Param_classesは、java値(pc, *)のvecでなければならない。ここでpcは、java.lang.Classのインスタンスでなければならない。
Ret_classは、java値(rc, *)でなければならない。ここでrcは、java.lang.Classのインスタンスでなければならない。
6.72.4. METHOD_HANDLE.constructor(Owner_class Param_classes ...[$config={}])¶
constructorは、publicコンストラクタのメソッドハンドルを検索する。
コンフィグメソッド:
• C.on_success($success): default = VAL.identity
• C.on_no_such_method($no_such_method): default = 例外を投げる関数
• C.on_illegal_access($illegal_access): default = 例外を投げる関数
constructorは、MethodHandles.publicLookup().findConstructorを使ってコンストラクタを検索する。ここで、Owner_classをコンストラクタの持ち主として、Param_classesをパラメータ型の列として使う。
結果
コンストラクタが見つかった場合、constructorは、java値(mh, MethodHandle)を引数として$successを末尾呼び出しする。ここでmhは、コンストラクタのメソッドハンドルである。メソッドハンドルのパラメータ型の列はParam_classes, 戻り型はOwner_classになる。
NoSuchMethodExceptionの例外nsmeが投げられた場合、constructorは、java値(nsme, NoSuchMethodException)を引数として$no_such_methodを末尾呼び出しする。
IllegalAccessExceptionの例外iaeが投げられた場合、constructorは、java値(iae, IllegalAccessException)を引数として$illegal_accessを末尾呼び出しする。
事前条件
Owner_classは、java値(oc, *)でなければならない。ここでocは、java.lang.Classのインスタンスでなければならない。
Param_classesは、java値(pc, *)のvecでなければならない。ここでpcは、java.lang.Classのインスタンスでなければならない。
$success, $no_such_method, $illegal_accessは、java値を取る関数でなければならない。
6.72.5. METHOD_HANDLE.proxy(Param_classes Ret_class $handler)¶
proxyは、Kinkの関数$handlerに手続きを委譲するメソッドハンドルを戻す。
結果はjava値(mh, MethodHandle)になる。ここでmhは、$handlerに手続きを委譲するメソッドハンドルである。メソッドハンドルのパラメータ型の列はParam_classesになり、戻り型はRet_classになる。
メソッドハンドルの呼び出し
メソッドハンドルが呼び出されると、新しい抽象スタックマシンが作られ、その中で$handlerが呼び出される。
呼び出しの引数列は、$handlerに引数列として渡される。引数はjava値(arg, argType)として渡される。ここで、argTypeはメソッドハンドルの対応するパラメータ型である。argTypeが参照型のとき、argは渡された引数それ自体である。argTypeがプリミティブ型のとき、argはその引数をラップするラッパインスタンスである。
呼び出しから戻る
メソッドハンドルの戻り型が参照型のとき、呼び出しがその参照型の値refを戻すには、$handlerはjava値(ref, *)を戻さなければならない。
メソッドハンドルの戻り型がchar, boolean, byte, short, int, long, float, doubleのいずれかのとき、呼び出しが値primitiveを戻すには、$handlerはjava値(wrapper, *)を戻さなければならない。ここでwrapperは、primitiveをラップするラッパインスタンスでなければならない。
メソッドハンドルの戻り型がvoidのとき、呼び出しが戻るには、$handlerはjava_throw以外の値を戻さなければならない。
呼び出しからJavaのThrowableを投げる
呼び出しがJavaのThrowableを投げるには、$handlerはjava_throwを戻さなければならない。
事前条件
Param_classesは、java値(pc, *)のvecでなければならない。ここでpcは、java.lang.Classのインスタンスでなければならない。
Ret_classは、java値(rc, *)でなければならない。ここでrcは、java.lang.Classのインスタンスでなければならない。
例: intを戻す
:JAVA.require_from('kink/javahost/')
:METHOD_HANDLE.require_from('kink/javahost/')
:Mh <- METHOD_HANDLE.proxy([JAVA.int_class JAVA.int_class] JAVA.int_class){(:X :Y)
:Sum = X.to_kink_num + Y.to_kink_num
JAVA.int(Sum)
}
:R <- METHOD_HANDLE.invoke(Mh [JAVA.int(10) JAVA.int(20)])
stdout.print_line(R.repr) # => (java "30" as int)
例: 例外を投げる
:JAVA.require_from('kink/javahost/')
:JAVA_THROW.require_from('kink/javahost/')
:METHOD_HANDLE.require_from('kink/javahost/')
:RuntimeException_class <- JAVA.class('java.lang.RuntimeException')
:Mh <- METHOD_HANDLE.proxy([] JAVA.void_class){
JAVA_THROW.new(RuntimeException_class.new(JAVA.string('bang!')))
}
METHOD_HANDLE.invoke(Mh []){(:C)
C.catch(RuntimeException_class){(:Rte)
stdout.print_line('thrown: {}'.format(Rte.repr))
}
}
# => thrown: (java "java.lang.RuntimeException: bang!" as java.lang.RuntimeException)