Hi, I'm delving into Kotlin/Wasm internals out of ...
# webassembly
r
Hi, I'm delving into Kotlin/Wasm internals out of curiosity, really a great work! (I also watched/read the presentation on the introduction to Kotlin/Wasm at Wasm/IO, which greatly helped me understand the internals 🙂 https://seb.deleuze.fr/introducing-kotlin-wasm/). I have a question regarding interface dispatching. When we have a class like
class Derived: Base
, it's vtable's type is sth like
(field (ref $Derived.vtable___type_30))
. On the other hand, the itable's type is always
(field (ref null struct))
and an interface dispatch always requires
ref.cast
at the call site, as mentioned in the presentation. My question is: Why don't we define a class with a concrete itable type, and remove the
ref.cast
at call site? Can we replace
val classITableRefGcType = WasmRefNullType(WasmHeapType.Simple.Struct)
with
val classITableRefGcType = WasmRefNullType(WasmHeapType.Type(context.referenceClassITableGcType(symbol)))
in https://github.com/JetBrains/kotlin/blob/c4fc0b919dfe9b746f9ecfcc08668fa05b839064/[…]g/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt ?
s
Hi! We currently lower all static interface types to
kotlin.Any
, requiring at least one cast at the call site. In cases where we know concrete static class type, we bypass the itable and generate either direct (final) or vtable (open) call.
👍 1
The idea of having different wasm type for interfaces is indeed interesting, but requires bigger changes to the backend.
r
Thank you for your response! Oh yeah, that's right, I realize I may not have fully grasped the concept of interface dispatch 😅 I was curious it might be possible to do that because J2CL-generated wasm has concrete itable types For example, for class and interface like
Copy code
interface Animal {
  public void sound();
}

class Cat implements Animal {
  public void sound() {}
}
Cat
's type and itable are like
Copy code
(type $<http://com.google.j2cl.samples.wasm.Cat|com.google.j2cl.samples.wasm.Cat> (sub $java.lang.Object (struct
  (field $vtable (ref $com.google.j2cl.samples.wasm.Cat.vtable))
  (field $itable (ref $com.google.j2cl.samples.wasm.Cat.itable))
  (field $$systemIdentityHashCode@java.lang.Object (mut i32))
  ))
 )

 (type $com.google.j2cl.samples.wasm.Cat.itable (sub $itable (struct
 (field $slot0 (ref $com.google.j2cl.samples.wasm.Animal.vtable))
 (field $slot1 (ref null struct))
 (field $slot2 (ref null struct))
 (field $slot3 (ref null struct))
 (field $slot4 (ref null struct))
 (field $slot5 (ref null struct))
 (field $slot6 (ref null struct))
 )))
However, it turned out J2CL still requires casting at call-site. 😃 Since, as you mentioned, the static interfaces are represented as
java.lang.Object
(like
kotlin.Any
for Kotlin), at least one cast is required. (not sure which virtual methods are available from it's vtable at interface call site.)
For a method like
Copy code
private static void doSound(Animal animal) {
    animal.sound();
}
it's gonna be compiled to like
Copy code
(func $m_doSound__com_google_j2cl_samples_wasm_Animal__void@com.google.j2cl.samples.wasm.HelloWorld
 (param $animal (ref null $java.lang.Object))
  (call_ref $function.m_sound__void
    (ref.as_non_null (local.get $animal))
    (struct.get
      $com.google.j2cl.samples.wasm.Animal.vtable
      $m_sound__void
      (ref.cast
        (ref $com.google.j2cl.samples.wasm.Animal.vtable)
        (struct.get
          $itable
          $slot0
          (struct.get
            $java.lang.Object
            $itable
            (local.get $animal)
          )
        )
      )
    )
  )
)