Wasm Spec Kernel

This is an example notebook demonstrating some of the features in the Wasm Spec Kernel. The kernel is barebones, it's a simple wrapper around the REPL provided by the Wasm reference interpreter. All commands are run directly in that REPL, as though they were typed in to it directly.

Hanging Execution

Incomplete code will cause the REPL to hang, such as (module $incomplete (notice the lack of a closing parenthesis). You can kill hanging executions by hitting stop or interrupt, which will cause the entire REPL process to be rebooted.

Example Module

This notebook includes a simple Wasm module, $lib_math, implementing a recursive fibonacci algorithm, which is exported as $fibonacci_rec. It then triggers this exported function to calculate the 10th number in the fibonacci sequence.

Example Errors

Finally, invalid Wasm code is provided to showcase what an error looks like. Notice that the line numbers (e.g. stdin:8.19-8.35) do not correspond to the line numbers in the code sample. Instead, they correspond to the cumulative line numbers received by the REPL. The kernel does not attempt to translate these values and instead passes information directly to and from the REPL, as provided.

In [1]:
(module $lib_math
    (func $fib_rec (export "fibonacci_rec") (param $n i32) (result i32)
        (if (result i32) (i32.lt_s (local.get $n) (i32.const 2))
            (then (local.get $n))
            (else (i32.add (call $fib_rec (i32.sub (local.get $n) (i32.const 1)))
                           (call $fib_rec (i32.sub (local.get $n) (i32.const 2))))))))
module $lib_math :
  export func "fibonacci_rec" : [i32] -> [i32]
In [2]:
(invoke $lib_math "fibonacci_rec" (i32.const 10))
55 : i32
In [3]:
(module $invalid (not_an_operation))
stdin:8.19-8.35: syntax error: unknown operator

Appendix

Sample: Wasm + Abstract Types

@awendland originally created this kernel to share code samples leveraging an extension of the WebAssembly language that included abstract types (see awendland/webassembly-spec-abstypes. This kernel supports any build of the WebAssembly reference interpreter, but this particular notebook environment has been configured to use a build with the abstypes extension.

Here's an example with abstypes. First, pseudo-source OCaml that theoretically could have produced the following Wasm:

(* lib.ml *)
module Date = sig
  type date (* public, abstract type *)
end =
struct
  type date = {day : int;  month : int;  year : int} (* private, concrete type *)
  val create : ?days:int -> ?months:int -> ?years:int -> unit -> date
  val yearsBetweenDates : date -> date -> int
  val month : date -> int
  ...
end

(* consumer.ml *)
let kjohnson_bday : Date.date = Date.create 8 26 1918 () in
let mercury_launch : Date.date = Date.create 2 20 1962 () in
let kj_age_at_launch = Date.yearsBetweenDates kjohnson_bday mercury_launch in ...
(* kjognson_bday.day <- this access is invalid *)

Next, a WebAssembly implementation that leverages abstract types to enforce the same invariants:

In [4]:
;; ======
;; Demo 2.2 - example date library
;;
;; Reference: Section 3.2 in the thesis (and also Section 3.4)
;; Dependencies: Core WebAssembly + abstract types (https://github.com/awendland/webassembly-spec-abstypes)
;;
;; NOTE: This sample is a poor implementation for a date library because it incorrectly assumes that 1 year is always 31,557,600,000 milliseconds. Do not use it.
;; ======

(module $lib_date
  (abstype_new $Date i32)
  (func (export "createDate")
    (param $day i32) (param $month i32) (param $year i32)
    (result (abstype_new_ref $Date))
    (i32.add ;; Day, Mon, Year -> Unix milliseconds
      (i32.mul (local.get $day) (i32.const 86400))
      (i32.add
        (i32.mul (local.get $month) (i32.const 2592000))
        (i32.mul (i32.const 31557600)
          (i32.sub (local.get $year) (i32.const 1970)))))
  )
  (func (export "yearsBetweenDates") (param (abstype_new_ref $Date))
    (param (abstype_new_ref $Date)) (result i32)
    (i32.sub (local.get 0) (local.get 1))
    (i32.div_s (i32.const 31557600))
  )
  (export "Date" (abstype_new_ref $Date))
)
(register "lib_date" $lib_date)

(module $main
  (import "lib_date" "Date" (abstype_sealed $Date))
  (import "lib_date" "createDate" (func $createDate
    (param i32) (param i32) (param i32)
    (result (abstype_sealed_ref $Date))))
  (import "lib_date" "yearsBetweenDates" (func $yearsBetweenDates
    (param (abstype_sealed_ref $Date))
    (param (abstype_sealed_ref $Date)) (result i32)))
  (func (export "main") (result i32)
    (call $createDate
      (i32.const 2) (i32.const 20) (i32.const 1962))
    (call $createDate
      (i32.const 8) (i32.const 26) (i32.const 1918))
    (call $yearsBetweenDates)
  )
)
(invoke $main "main")
module $lib_date :
  export func "createDate" : [i32 i32 i32] -> [abs{0}]
  export func "yearsBetweenDates" : [abs{0} abs{0}] -> [i32]
  export abstype "Date" : abs{0}
module $main :
  import abstype "lib_date" "Date" : abs{'Date','lib_date'}
  import func "lib_date" "createDate" : [i32 i32 i32] -> [abs{'Date','lib_date'}]
  import func "lib_date" "yearsBetweenDates" : [abs{'Date','lib_date'} abs{'Date','lib_date'}] -> [i32]
  export func "main" : [] -> [i32]
43 : i32