Scala.js でサポートされている reflective call
subtypingInReturnTypes
code:scala
@Test def subtypingInReturnTypes(): Unit = {
class A { def x: Int = 1 }
class B extends A { override def x: Int = 2 }
object Generator {
def generate(): B = new B
}
def f(x: { def generate(): A }): A = x.generate
assertEquals(2, f(Generator).x)
}
code:scala
linked Class sample.Main$Generator$1$ extends java.lang.Object {
def generate;Lsample.Main$B$1(): sample.Main$B$1 = {
new sample.Main$B$1().<init>;V()
}
constructor def <init>;V() {
this.java.lang.Object::<init>;V()
}
def generate;R(): any = {
this.generate;Lsample.Main$B$1()
}
}
linked ModuleClass sample.Main$ extends java.lang.Object {
// ...
def subtypingInReturnTypes;Z(): boolean = {
val Generator$module: scala.runtime.LazyRef = new scala.runtime.LazyRef().<init>;V();
(2 ==int this.sample.Main$::private::f$1;Ljava.lang.Object;Lsample.Main$A$1( // returns sample.Main$Generator$1$
this.sample.Main$::private::Generator$2;Lscala.runtime.LazyRef;Lsample.Main$Generator$1$(
Generator$module
)
).x;I())
}
// ...
private def Generator$lzycompute$1;Lscala.runtime.LazyRef;Lsample.Main$Generator$1$(Generator$module$1: scala.runtime.LazyRef): sample.Main$Generator$1$ = {
Generator$module$1.getClass();
if (Generator$module$1.initialized;Z()) {
} else {
Generator$module$1.initialize;Ljava.lang.Object;Ljava.lang.Object(new sample.Main$Generator$1$().<init>;V()).asInstanceOfsample.Main$Generator$1$ }
}
// Generator (LazyRef って何?) なんか初期化するくん
private def Generator$2;Lscala.runtime.LazyRef;Lsample.Main$Generator$1$(Generator$module$1: scala.runtime.LazyRef): sample.Main$Generator$1$ = {
if (Generator$module$1.initialized;Z()) {
} else {
this.sample.Main$::private::Generator$lzycompute$1;Lscala.runtime.LazyRef;Lsample.Main$Generator$1$(Generator$module$1)
}
}
// def f(x: { def generate(): A }): A = x.generate
private def f$1;Ljava.lang.Object;Lsample.Main$A$1(x: any): sample.Main$A$1 = {
{
val x$2: any = x;
x$2.generate;R() // def generate;R(): any (call .generate on any?)
}
...
}
code:scala
Apply(org.scalajs.ir.Trees$ApplyFlags@0,VarRef(LocalIdent(LocalName<x$2>)),MethodIdent(MethodName<generate;R>),List())
reflective call も普通の Apply になっていそう?
問題は any の vtable には当然 generate はないのである!
どうやってこれが reflective call だと判定する? any に対するメソッド呼び出しは reflective call とみなすことができる?
hr.icon
code:scala
linked ModuleClass sample.Main$ extends java.lang.Object {
def main;[Ljava.lang.String;V(args: java.lang.String[]) {
this.thisTypeInReturnTypes;V()
}
def thisTypeInReturnTypes;V() {
this.sample.Main$::private::f$1;Ljava.lang.Object;Ljava.lang.Object(new sample.Main$StringValue$1().<init>;Ljava.lang.String;V("foo")).toString;Ljava.lang.String()
}
private def f$1;Ljava.lang.Object;Ljava.lang.Object(x: any): any = {
val x$2: any = x;
x$2.value;R()
}
constructor def <init>;V() {
this.java.lang.Object::<init>;V();
<storeModule>
}
}
code:scala
linked ModuleClass sample.Main$ extends java.lang.Object {
def main;[Ljava.lang.String;V(args: java.lang.String[]) {
this.thisTypeInReturnTypes;V()
}
def thisTypeInReturnTypes;V() {
this.sample.Main$::private::f$1;Ljava.lang.Object;Ljava.lang.Object(new sample.Main$StringValue$1().<init>;Ljava.lang.String;V("foo")).toString;Ljava.lang.String()
}
// def f(x: ValueType): ValueType = x.value
private def f$1;Ljava.lang.Object;Ljava.lang.Object(x: any): any = {
val x$2: any = x;
x$2.value;R()
}
constructor def <init>;V() {
this.java.lang.Object::<init>;V();
<storeModule>
}
}
hr.icon
primitives や compareTo
def fShort(x: Any { def +(x: Short): Int }): Int = x + 6.toShort
def fCompareToBoolean(x: { def compareTo(y: java.lang.Boolean): Int }, y: Boolean): Int
def concat(x: Any { def +(y: String): String }, y: String): String = x + y
(x$2: any).$plus;Ljava.lang.String;R(y.asInstanceOf[java.lang.String])
$plus;Ljava.lang.String;R の定義は? R って何?
isReflectiveProxy!!! って何?
こういうのは暗黙的に? 定義されているので困りそう
array のメソッドへの reflective call など、string などの primitive も
code:scala
type UPD = { def update(i: Int, x: String): Unit }
type APL = { def apply(i: Int): String }
type LEN = { def length: Int }
type CLONE = Any { def clone(): Object }
def upd(obj: UPD, i: Int, x: String): Unit = obj.update(i,x)
def apl(obj: APL, i: Int): String = obj.apply(i)
def len(obj: LEN): Int = obj.length
def clone(obj: CLONE): Object = obj.clone
hr.icon
code:scala
@Test def forwardersForInheritedMethods(): Unit = {
trait A {
def foo: Int
}
abstract class B extends A
class C extends B {
def foo: Int = 1
}
def call(x: { def foo: Int }): Int = x.foo
assertEquals(1, call(new C))
}
code:scala
def forwardersForInheritedMethods;V() {
this.assertEquals;Ljava.lang.Object;Ljava.lang.Object;Z(1, this.sample.Main$::private::call$1;Ljava.lang.Object;I(new sample.Main$C$1().<init>;V()))
}
private def call$1;Ljava.lang.Object;I(x: any): int = {
{
val x$2: any = x;
x$2.foo;R()
}
hr.icon
Object.notify, notifyAll, clone, eq, ne, synchronized, Float.isNan
code:scala
type ObjNotifyLike = Any {
def notify(): Unit
def notifyAll(): Unit
}
def objNotifyTest(obj: ObjNotifyLike): Int = {
obj.notify()
obj.notifyAll()
1
}
type ObjCloneLike = Any { def clone(): AnyRef }
def objCloneTest(obj: ObjCloneLike): AnyRef = obj.clone()
type ObjWithAnyRefPrimitives = Any {
def eq(that: AnyRef): Boolean
def ne(that: AnyRef): Boolean
def synchronizedT(f: T): Any }
type FloatingNumberLike = Any {
def isNaN(): Boolean
def isInfinite(): Boolean
}
つまり...
any に対する メソッドコール集めたらどうなりますかね
そのときはどこに dispatch するんですか?
vtable にはあるはずだが...
isReflectiveProxy な関数は table においておいて、コンパイル時には reflective call の名前から table を探索すれば...
しかし複数の同名 reflectiveProxy があることもあるのでは? class がわかればいいのだが実行時までわからん
signature は同じはずだしなぁ (全部 any を receiver と return type とするはず)