Skip to content

Instantly share code, notes, and snippets.

@tanishiking
Last active February 20, 2024 09:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tanishiking/df2295cc63fa311a08bb8f94c4d88e49 to your computer and use it in GitHub Desktop.
Save tanishiking/df2295cc63fa311a08bb8f94c4d88e49 to your computer and use it in GitHub Desktop.

https://github.com/tanishiking/scala-wasm

  • Basic mathematics
  • class
  • object (load module / store module)
  • static function call
  • virtual call
  • interface call
  • loop
  • if/switch
  • try catch
  • data class
    • string
    • array
  • export
    • toplevel-export method
    • toplevel module
  • JS call
// Sample.scala
package sample

import scala.scalajs.js.annotation._

object Main extends Base {
  def multiply(x: Int, y: Int) = x * y

  @JSExportTopLevel("multSqrt")
  def multSqrt(x: Int, y: Int) =
    multiply(sqrt(x), sqrt(y))
}

class Base {
  def sqrt(x: Int) = x * x
}

sbt sample/run will generate WAT to stdio

Base class type definiton

(type $fun_type___0___ty (func ;; Base#sqrt method's type
  (param (ref null $sample.Base___struct)) ;; receiver
  (param i32) ;; x: Int
  (result i32))
 )
 
;; struct for Base class
(type $sample.Base___struct (sub (struct 
  (field $vtable___field (ref null $sample.Base___vtable)))) ;; reference to Base class's vtable
)
 
(type $sample.Base___vtable (sub (struct ;; struct for Base class vtable
  (field $sample.Base#sqrt_I_I___field (ref null $fun_type___0___ty)))) ;; function reference to Base#sqrt
)

Base class implementation

(func $sample.Base#sqrt_I_I___fun (type $fun_type___0___ty)
 (param $<this>___local (ref null $sample.Base___struct)) (param $x___local i32) (result i32)
 local.get $x___local
 local.get $x___local
 i32.mul
 return
)

;; Base class has a global vtable
(global $sample.Base___g_vtable (ref null $sample.Base___vtable)
   ref.func $sample.Base#sqrt_I_I___fun
   struct.new $sample.Base___vtable
)

;; constructor will set the vtable to `sample.Base___struct`
(func $__sample.Base#newDefault___fun (type $fun_type___4___ty)
   (result (ref $sample.Base___struct))
   global.get $sample.Base___g_vtable
   struct.new $sample.Base___struct
  )
(module
  ;; ...
  (export "multSqrt" (func $multSqrt#___fun)))
)
(module
  (rec
    (type $sample.Base___vtable (sub (struct (field $sample.Base#sqrt_I_I___field (ref null $fun_type___0___ty)))))
    (type $sample.Base___struct (sub (struct (field $vtable___field (ref null $sample.Base___vtable)))))
    (type $sample.Main$___vtable (sub $sample.Base___vtable (struct (field $sample.Base#sqrt_I_I___field (ref null $fun_type___0___ty)) (field $sample.Main$#multiply_I_I_I___field (ref null $fun_type___1___ty)) (field $sample.Main$#multSqrt_I_I_I___field (ref null $fun_type___1___ty)))))
    (type $sample.Main$___struct (sub $sample.Base___struct (struct (field $vtable___field (ref null $sample.Main$___vtable)))))
    (type $sample.Main___vtable (sub (struct (field $sample.Main#multSqrt_I_I_I___field (ref null $fun_type___2___ty)) (field $sample.Main#multiply_I_I_I___field (ref null $fun_type___2___ty)) (field $sample.Main#sqrt_I_I___field (ref null $fun_type___3___ty)))))
    (type $sample.Main___struct (sub (struct (field $vtable___field (ref null $sample.Main___vtable)))))
    (type $fun_type___0___ty (func (param (ref null $sample.Base___struct)) (param i32) (result i32)))
    (type $fun_type___1___ty (func (param (ref null $sample.Main$___struct)) (param i32) (param i32) (result i32)))
    (type $fun_type___2___ty (func (param (ref null $sample.Main___struct)) (param i32) (param i32) (result i32)))
    (type $fun_type___3___ty (func (param (ref null $sample.Main___struct)) (param i32) (result i32)))
    (type $fun_type___4___ty (func (result (ref $sample.Base___struct))))
    (type $fun_type___5___ty (func (param (ref null $sample.Base___struct))))
    (type $fun_type___6___ty (func (result (ref $sample.Main$___struct))))
    (type $fun_type___7___ty (func (param (ref null $sample.Main$___struct))))
    (type $fun_type___8___ty (func (result (ref null $sample.Main$___struct))))
    (type $fun_type___9___ty (func (param i32) (param i32) (result i32)))
    (type $fun_type___10___ty (func (result (ref $sample.Main___struct)))))
  (func $__sample.Base#newDefault___fun (type $fun_type___4___ty)
     (result (ref $sample.Base___struct))
     global.get $sample.Base___g_vtable
     struct.new $sample.Base___struct
    )
  (func $sample.Base#sqrt_I_I___fun (type $fun_type___0___ty)
     (param $<this>___local (ref null $sample.Base___struct)) (param $x___local i32) (result i32)
     local.get $x___local
     local.get $x___local
     i32.mul
     return
    )
  (func $sample.Base#<init>_V___fun (type $fun_type___5___ty)
     (param $<this>___local (ref null $sample.Base___struct))
     local.get $<this>___local
     ref.is_null
     if
     call $__sample.Base#newDefault___fun
     local.set $<this>___local
     end
    )
  (func $__sample.Main$#newDefault___fun (type $fun_type___6___ty)
     (result (ref $sample.Main$___struct))
     global.get $sample.Main$___g_vtable
     struct.new $sample.Main$___struct
    )
  (func $sample.Main$#multiply_I_I_I___fun (type $fun_type___1___ty)
     (param $<this>___local (ref null $sample.Main$___struct)) (param $x___local i32) (param $y___local i32) (result i32)
     local.get $x___local
     local.get $y___local
     i32.mul
     return
    )
  (func $sample.Main$#multSqrt_I_I_I___fun (type $fun_type___1___ty)
     (param $<this>___local (ref null $sample.Main$___struct)) (param $x___local i32) (param $y___local i32) (result i32)
     local.get $<this>___local
     local.get $<this>___local
     local.get $x___local
     local.get $<this>___local
     struct.get $sample.Main$___struct 0
     struct.get $sample.Main$___vtable 0
     call_ref $fun_type___0___ty
     local.get $<this>___local
     local.get $y___local
     local.get $<this>___local
     struct.get $sample.Main$___struct 0
     struct.get $sample.Main$___vtable 0
     call_ref $fun_type___0___ty
     local.get $<this>___local
     struct.get $sample.Main$___struct 0
     struct.get $sample.Main$___vtable 1
     call_ref $fun_type___1___ty
     return
    )
  (func $sample.Main$#<init>_V___fun (type $fun_type___7___ty)
     (param $<this>___local (ref null $sample.Main$___struct))
     local.get $<this>___local
     ref.is_null
     if
     call $__sample.Main$#newDefault___fun
     local.set $<this>___local
     end
     local.get $<this>___local
     call $sample.Base#<init>_V___fun
     local.get $<this>___local
     global.set $sample.Main$___g_instance
    )
  (func $__sample.Main$#loadModule___fun (type $fun_type___8___ty)
     (result (ref null $sample.Main$___struct))
     global.get $sample.Main$___g_instance
     ref.is_null
     if
     global.get $sample.Main$___g_instance
     call $sample.Main$#<init>_V___fun
     end
     global.get $sample.Main$___g_instance
    )
  (func $multSqrt#___fun (type $fun_type___9___ty)
     (param $arg___local i32) (param $arg$2___local i32) (result i32)
     (local $prep0___local i32) (local $prep1___local i32)
     local.get $arg___local
     local.set $prep0___local
     local.get $arg$2___local
     local.set $prep1___local
     call $__sample.Main$#loadModule___fun
     local.get $arg___local
     local.get $arg$2___local
     call $__sample.Main$#loadModule___fun
     struct.get $sample.Main$___struct 0
     struct.get $sample.Main$___vtable 2
     call_ref $fun_type___1___ty
    )
  (func $__sample.Main#newDefault___fun (type $fun_type___10___ty)
     (result (ref $sample.Main___struct))
     global.get $sample.Main___g_vtable
     struct.new $sample.Main___struct
    )
  (func $sample.Main#multSqrt_I_I_I___fun (type $fun_type___2___ty)
     (param $<this>___local (ref null $sample.Main___struct)) (param $x___local i32) (param $y___local i32) (result i32)
     call $__sample.Main$#loadModule___fun
     local.get $x___local
     local.get $y___local
     call $__sample.Main$#loadModule___fun
     struct.get $sample.Main$___struct 0
     struct.get $sample.Main$___vtable 2
     call_ref $fun_type___1___ty
     return
    )
  (func $sample.Main#multiply_I_I_I___fun (type $fun_type___2___ty)
     (param $<this>___local (ref null $sample.Main___struct)) (param $x___local i32) (param $y___local i32) (result i32)
     call $__sample.Main$#loadModule___fun
     local.get $x___local
     local.get $y___local
     call $__sample.Main$#loadModule___fun
     struct.get $sample.Main$___struct 0
     struct.get $sample.Main$___vtable 1
     call_ref $fun_type___1___ty
     return
    )
  (func $sample.Main#sqrt_I_I___fun (type $fun_type___3___ty)
     (param $<this>___local (ref null $sample.Main___struct)) (param $x___local i32) (result i32)
     call $__sample.Main$#loadModule___fun
     local.get $x___local
     call $__sample.Main$#loadModule___fun
     struct.get $sample.Main$___struct 0
     struct.get $sample.Main$___vtable 0
     call_ref $fun_type___0___ty
     return
    )
  (global $sample.Base___g_vtable (ref null $sample.Base___vtable)
     ref.func $sample.Base#sqrt_I_I___fun
     struct.new $sample.Base___vtable
    )
  (global $sample.Main$___g_vtable (ref null $sample.Main$___vtable)
     ref.func $sample.Base#sqrt_I_I___fun
     ref.func $sample.Main$#multiply_I_I_I___fun
     ref.func $sample.Main$#multSqrt_I_I_I___fun
     struct.new $sample.Main$___vtable
    )
  (global $sample.Main$___g_instance (mut (ref null $sample.Main$___struct))
     ref.null $sample.Main$___struct
    )
  (global $sample.Main___g_vtable (ref null $sample.Main___vtable)
     ref.func $sample.Main#multSqrt_I_I_I___fun
     ref.func $sample.Main#multiply_I_I_I___fun
     ref.func $sample.Main#sqrt_I_I___fun
     struct.new $sample.Main___vtable
    )
  (export "multSqrt" (func $multSqrt#___fun)))

Transform the generated WAT (test.wat) to Wasm binary using reference interpreter

https://github.com/tanishiking/wasmgc-docker

$ docker run -i -v "$PWD":/data wasmgc wasm -d /data/test.wat -o /data/test.wasm

And run it using Deno

// run.mjs
import { readFileSync } from "node:fs";
const wasmBuffer = readFileSync("test.wasm");
const wasmModule = await WebAssembly.instantiate(wasmBuffer);
const { multSqrt } = wasmModule.instance.exports;
const o = multSqrt(5, 10); // 5^2 * 10^2 == 25 * 100 = 2500
console.log(o);
$ deno --version
deno 1.38.3 (release, aarch64-apple-darwin)
v8 12.0.267.1
typescript 5.2.2

$ deno run --allow-read run.mjs
2500
@tanishiking
Copy link
Author

tanishiking commented Feb 16, 2024

  • None of standard libraries work yet (including String and Array)
  • Other ways (except top level export method) for exporting Scala.js methods don't work
  • Interface dispatching doesn't work (so interface and trait)
  • Direct translation to binary also doesn't work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment