Closed2

j2cl wasm の出力を眺める

tanishikingtanishiking

(なんか Bazel 5.4.0 だと動かなったので .bazelversion6.4.0 にしたら大丈夫だった、7.0もダメそう)

// google/j2cl/samples/wasm/src/main/java/com/google/j2cl/samples/wasm/HelloWorld.java
package com.google.j2cl.samples.wasm;

class Animal {
    String name;
    Animal(String name) { this.name = name; }
    void eat() { System.out.println(name + " is eating."); }
}

class Dog extends Animal {
    Dog(String name) { super(name); }
    String bark() { return "Woof! Woof!"; }
}

public class HelloWorld {
    public static String getHelloWorld() {
        Animal genericAnimal = new Animal("Generic Animal");
        genericAnimal.eat();

        System.out.println("---------------");

        Dog myDog = new Dog("Buddy");
        myDog.eat();
        return myDog.bark();
    }
}
$ cd samples/wasm
$ bazel build src/main/java/com/google/j2cl/samples/wasm:jsapp
$ cat bazel-out/darwin_arm64-fastbuild/bin/src/main/java/com/google/j2cl/samples/wasm/app.wat
 ;;; Code for com.google.j2cl.samples.wasm.Dog [type definition]
 (type $com.google.j2cl.samples.wasm.Dog (sub $com.google.j2cl.samples.wasm.Animal (struct
  (field $vtable (ref $com.google.j2cl.samples.wasm.Dog.vtable))
  (field $itable (ref $itable))
  (field $$systemIdentityHashCode@java.lang.Object (mut i32))
  (field $name@com.google.j2cl.samples.wasm.Animal (mut (ref null $java.lang.String)))
  ))
 )
 (type $com.google.j2cl.samples.wasm.Dog.vtable (sub $com.google.j2cl.samples.wasm.Animal.vtable (struct
  (field $$getClassImpl__java_lang_Class (ref $function.$getClassImpl__java_lang_Class))
  (field $m_equals__java_lang_Object__boolean (ref $function.m_equals__java_lang_Object__boolean))
  (field $m_getClass__java_lang_Class (ref $function.m_getClass__java_lang_Class))
  (field $m_hashCode__int (ref $function.m_hashCode__int))
  (field $m_toString__java_lang_String (ref $function.m_toString__java_lang_String))
  (field $m_eat__void_$pp_com_google_j2cl_samples_wasm (ref $function.m_eat__void_$pp_com_google_j2cl_samples_wasm))
  (field $m_bark__java_lang_String_$pp_com_google_j2cl_samples_wasm (ref $function.m_bark__java_lang_String_$pp_com_google_j2cl_samples_wasm))
  ))
 )
 ;;; End of code for com.google.j2cl.samples.wasm.Dog [type definition]



;;; String HelloWorld.getHelloWorld()
(func $m_getHelloWorld__java_lang_String@com.google.j2cl.samples.wasm.HelloWorld
 (result (ref null $java.lang.String))
 ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:37:25
 (local $genericAnimal (ref null $com.google.j2cl.samples.wasm.Animal))
 (local $$qualifier (ref null $java.io.PrintStream))
 (local $myDog (ref null $com.google.j2cl.samples.wasm.Dog))
 (block
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:37:41
  (call $$clinit__void_<once>_@com.google.j2cl.samples.wasm.HelloWorld )
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:38:8
  (local.set $genericAnimal (call $$create__java_lang_String@com.google.j2cl.samples.wasm.Animal (call $$getString_|Generic_Animal|__java_lang_String_<once>_@com.google.j2cl.samples.wasm.HelloWorld )))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:39:8
  (call_ref $function.m_eat__void_$pp_com_google_j2cl_samples_wasm (ref.as_non_null (local.get $genericAnimal))(struct.get $com.google.j2cl.samples.wasm.Animal.vtable $m_eat__void_$pp_com_google_j2cl_samples_wasm (struct.get $com.google.j2cl.samples.wasm.Animal $vtable(local.get $genericAnimal))))
  (block
   ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:41:8
   (local.set $$qualifier (block (result (ref null $java.io.PrintStream))
    (call $$clinit__void_<once>_@java.lang.System )
    (global.get $out@java.lang.System)
   ))
   ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:41:8
   (call_ref $function.m_println__java_lang_String__void (ref.as_non_null (local.get $$qualifier))(call $$getString_|_______________|__java_lang_String_<once>_@com.google.j2cl.samples.wasm.HelloWorld )(struct.get $java.io.PrintStream.vtable $m_println__java_lang_String__void (struct.get $java.io.PrintStream $vtable(local.get $$qualifier))))
  )
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:43:8
  (local.set $myDog (call $$create__java_lang_String@com.google.j2cl.samples.wasm.Dog (call $$getString_|Buddy|__java_lang_String_<once>_@com.google.j2cl.samples.wasm.HelloWorld )))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:44:8
  (call_ref $function.m_eat__void_$pp_com_google_j2cl_samples_wasm (ref.as_non_null (local.get $myDog))(struct.get $com.google.j2cl.samples.wasm.Animal.vtable $m_eat__void_$pp_com_google_j2cl_samples_wasm (struct.get $com.google.j2cl.samples.wasm.Animal $vtable(local.get $myDog))))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:45:8
  (return (call_ref $function.m_bark__java_lang_String_$pp_com_google_j2cl_samples_wasm (ref.as_non_null (local.get $myDog))(struct.get $com.google.j2cl.samples.wasm.Dog.vtable $m_bark__java_lang_String_$pp_com_google_j2cl_samples_wasm (struct.get $com.google.j2cl.samples.wasm.Dog $vtable(local.get $myDog)))))
 )
)

このへん見ると分かるけど、Kotlin/Wasm と同様に各オブジェクトは vtable や itable へのインスタンスを持っている形。

  (call_ref $function.m_eat__void_$pp_com_google_j2cl_samples_wasm (ref.as_non_null (local.get $myDog))(struct.get $com.google.j2cl.samples.wasm.Animal.vtable $m_eat__void_$pp_com_google_j2cl_samples_wasm (struct.get $com.google.j2cl.samples.wasm.Animal $vtable(local.get $myDog))))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:45:8
  (return (call_ref $function.m_bark__java_lang_String_$pp_com_google_j2cl_samples_wasm (ref.as_non_null (local.get $myDog))(struct.get $com.google.j2cl.samples.wasm.Dog.vtable $m_bark__java_lang_String_$pp_com_google_j2cl_samples_wasm (struct.get $com.google.j2cl.samples.wasm.Dog $vtable(local.get $myDog)))))
tanishikingtanishiking

https://zenn.dev/tanishiking/scraps/f5c9566bf67cad に少し書いたけど、j2clのinterface dispatchも似たような感じ

package com.google.j2cl.samples.wasm;

interface Animal {
  public void sound();
}

class Cat implements Animal {
  public void sound() {}
}

class Lion extends Cat implements Animal {
}

public class HelloWorld {
    public static String getHelloWorld() {
        Lion lion = new Lion();
        doSound(lion);
        return "hello";

    }

    private static void doSound(Animal animal) {
        animal.sound();
    }
}

これは

 ;;; Code for com.google.j2cl.samples.wasm.Animal [type definition]
 (type $com.google.j2cl.samples.wasm.Animal.vtable (sub (struct
  (field $m_sound__void (ref $function.m_sound__void))
  ))
 )
 ;;; End of code for com.google.j2cl.samples.wasm.Animal [type definition]

Cat

 ;;; Code for com.google.j2cl.samples.wasm.Cat [type definition]
 (type $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.vtable (sub $java.lang.Object.vtable (struct
  (field $$getClassImpl__java_lang_Class (ref $function.$getClassImpl__java_lang_Class))
  (field $m_equals__java_lang_Object__boolean (ref $function.m_equals__java_lang_Object__boolean))
  (field $m_getClass__java_lang_Class (ref $function.m_getClass__java_lang_Class))
  (field $m_hashCode__int (ref $function.m_hashCode__int))
  (field $m_toString__java_lang_String (ref $function.m_toString__java_lang_String))
  (field $m_sound__void (ref $function.m_sound__void))
  ))
 )
 (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))
 )))
 ;;; End of code for com.google.j2cl.samples.wasm.Cat [type definition]

HelloWorld

;;; String HelloWorld.getHelloWorld()
(func $m_getHelloWorld__java_lang_String@com.google.j2cl.samples.wasm.HelloWorld
 (result (ref null $java.lang.String))
 ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:30:25
 (local $lion (ref null $com.google.j2cl.samples.wasm.Lion))
 (block
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:30:41
  (call $$clinit__void_<once>_@com.google.j2cl.samples.wasm.HelloWorld )
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:31:8
  (local.set $lion (call $$create__@com.google.j2cl.samples.wasm.Lion ))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:32:8
  (call $m_doSound__com_google_j2cl_samples_wasm_Animal__void@com.google.j2cl.samples.wasm.HelloWorld (local.get $lion))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:33:8
  (return (call $$getString_|hello|__java_lang_String_<once>_@com.google.j2cl.samples.wasm.HelloWorld ))
 )
)
  • $$clinit__void_<once>_@com.google.j2cl.samples.wasm.HelloWorld これはなんだろう、各クラスの何かの呼び出しの最初に実行されてそう。
  • $$create__@com.google.j2cl.samples.wasm.Lion で Lion インスタンスを作る
  • $m_doSound__com_google_j2cl_samples_wasm_Animal__void@com.google.j2cl.samples.wasm.HelloWorld (名前長い!!!) を local.get $lion を引数にして call
  • 文字列作ってreturn
;;; void HelloWorld.$clinit()
(func $$clinit__void_<once>_@com.google.j2cl.samples.wasm.HelloWorld
 ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:29:13
 (block
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:29:13
  (if (global.get $$class-initialized@com.google.j2cl.samples.wasm.HelloWorld)
   (then
    ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:29:13
    (return )
   )
  )
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:29:13
  (global.set $$class-initialized@com.google.j2cl.samples.wasm.HelloWorld (i32.const 1))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:29:13
  (call $$clinit__void_<once>_@java.lang.Object )
 )
)

なんだこれは? module initialize 的なやつが入るのかな?

Lion constructor

;;; Lion Lion.$create()
(func $$create__@com.google.j2cl.samples.wasm.Lion
 (result (ref null $com.google.j2cl.samples.wasm.Lion))
 ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:26:6
 (local $$instance (ref null $com.google.j2cl.samples.wasm.Lion))
 (block
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:26:6
  (call $$clinit__void_<once>_@com.google.j2cl.samples.wasm.Lion )
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:26:6
  (local.set $$instance (struct.new $com.google.j2cl.samples.wasm.Lion (ref.as_non_null (global.get $com.google.j2cl.samples.wasm.Lion.vtable)) (ref.as_non_null (global.get $com.google.j2cl.samples.wasm.Lion.itable)) (i32.const 0)))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:26:6
  (call $$ctor__void_$p_com_google_j2cl_samples_wasm_Lion@com.google.j2cl.samples.wasm.Lion (ref.as_non_null (local.get $$instance)))
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:26:6
  (return (local.get $$instance))
 )
)

$$instancestruct.new ... で vtable や itable などを追加して constructor を実行

;;; void Lion.$ctor()
(func $$ctor__void_$p_com_google_j2cl_samples_wasm_Lion@com.google.j2cl.samples.wasm.Lion
 (param $this (ref null $com.google.j2cl.samples.wasm.Lion))
 ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:26:6
 (block
  ;;@ com/google/j2cl/samples/wasm/HelloWorld.java:26:6
  (call $$ctor__void_$p_com_google_j2cl_samples_wasm_Cat@com.google.j2cl.samples.wasm.Cat (ref.as_non_null (local.get $this)))
 )
)

interface dispatch

;;; void HelloWorld.doSound(Animal animal)
(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)
          )
        )
      )
    )
  )
)

static interface は java.lang.Object になってる。ref.cast で itable の slot を vtable に変換して call_ref

このスクラップは4ヶ月前にクローズされました