Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[interpreter] Change how wast.js is built by using Js_of_ocaml #1507

Merged
merged 9 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ jobs:
- run: opam install --yes ocamlbuild.0.14.0
- run: cd interpreter && opam exec make all

ref-interpreter-js-library:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup OCaml
uses: ocaml/setup-ocaml@v2
with:
ocaml-compiler: 4.12.x
- run: opam install --yes ocamlbuild.0.14.0 ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0
- run: cd interpreter && opam exec make wast.js

build-js-api-spec:
runs-on: ubuntu-latest
steps:
Expand Down
21 changes: 10 additions & 11 deletions interpreter/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# package manager to build. However, Opam package management is available
# optionally through the check/install/uninstall targets.
#
# The $(JSLIB) target requires node.js and BuckleScript.
# The $(JSLIB).js target requires Js_of_ocaml (using ocamlfind).
#
# See README.me for instructions.

Expand All @@ -14,14 +14,15 @@ UNOPT = $(NAME).debug
OPT = $(NAME)
LIB = $(NAME)
ZIP = $(NAME).zip
JSLIB = wast.js
JSLIB = wast
WINMAKE = winmake.bat

DIRS = util syntax binary text valid runtime exec script host main tests
LIBS = bigarray
FLAGS = -lexflags -ml -cflags '-w +a-4-27-42-44-45-70 -warn-error +a-3'
OCBA = ocamlbuild $(FLAGS) $(DIRS:%=-I %)
OCB = $(OCBA) $(LIBS:%=-libs %)
JSO = js_of_ocaml -q --opt 3
JS = # set to JS shell command to run JS tests


Expand All @@ -35,7 +36,7 @@ opt: $(OPT)
unopt: $(UNOPT)
libopt: _build/$(LIB).cmx _build/$(LIB).cmxa
libunopt: _build/$(LIB).cmo _build/$(LIB).cma
jslib: $(JSLIB)
jslib: $(JSLIB).js
all: unopt opt libunopt libopt test
land: $(WINMAKE) all
zip: $(ZIP)
Expand Down Expand Up @@ -108,14 +109,12 @@ _build/$(LIB).cmxa: $(FILES) $(LIB).mllib _tags Makefile

# Building JavaScript library

.PHONY: $(JSLIB)
$(JSLIB): $(UNOPT)
mkdir -p _build/jslib/src
cp meta/jslib/* _build/jslib
cp $(DIRS:%=_build/%/*.ml*) meta/jslib/*.ml _build/jslib/src
rm _build/jslib/src/*.ml[^i]
(cd _build/jslib; ./build.sh ../../$@)
.INTERMEDIATE: $(JSLIB).byte
$(JSLIB).byte: meta/jslib/$(JSLIB).ml
$(OCBA) -use-ocamlfind -pkg js_of_ocaml -pkg js_of_ocaml-ppx meta/jslib/$(JSLIB).byte
takikawa marked this conversation as resolved.
Show resolved Hide resolved

$(JSLIB).js: $(JSLIB).byte
$(JSO) $(JSLIB).byte
takikawa marked this conversation as resolved.
Show resolved Hide resolved

# Building Windows build file

Expand Down Expand Up @@ -181,7 +180,7 @@ $(ZIP): $(WINMAKE)
git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD

clean:
rm -rf _build/jslib $(LIB).mlpack _tags
rm -rf _build/jslib $(LIB).mlpack _tags $(JSLIB).js
$(OCB) -clean


Expand Down
23 changes: 20 additions & 3 deletions interpreter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ The Makefile also provides a target to compile (parts of) the interpreter into a
```
make wast.js
```
Building this target requires node.js and BuckleScript.
Building this target requires `js_of_ocaml`, which can be installed with OPAM:
```
opam install js_of_ocaml js_of_ocaml-ppx
```


## Synopsis
Expand Down Expand Up @@ -139,7 +142,7 @@ WebAssemblyText.encode(source)
```
which turns a module in S-expression syntax into a WebAssembly binary, and
```
WebAssemblyText.decode(binary, width = 80)
WebAssemblyText.decode(binary, width)
```
which pretty-prints a binary back into a canonicalised S-expression string.

Expand All @@ -151,7 +154,7 @@ let binary = WebAssemblyText.encode(source)
(new WebAssembly.Instance(new WebAssembly.Module(binary))).exports.f(3, 4)
// => 7

WebAssemblyText.decode(binary)
WebAssemblyText.decode(binary, 80)
// =>
// (module
// (type $0 (func (param i32 i32) (result i32)))
Expand All @@ -160,6 +163,20 @@ WebAssemblyText.decode(binary)
// )
```

Depending on how you load the library, the object may be accessed in different ways. For example, using `require` in node.js:

```
let wast = require("./wast.js");
let binary = wast.WebAssemblyText.encode("(module"));
takikawa marked this conversation as resolved.
Show resolved Hide resolved
```

Or using `load` from a JavaScript shell:

```
load("./wast.js");
let binary = WebAssemblyText.encode("(module"));
takikawa marked this conversation as resolved.
Show resolved Hide resolved
```


## S-Expression Syntax

Expand Down
5 changes: 2 additions & 3 deletions interpreter/dune
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
(name wasm)
; The 'main' module shall not be part of the library, as it would start the
; Wasm REPL every time in all the dependencies.
; We also need to exclude the 'wasm' module as it overlaps with the library
; name.
; We exclude the 'wast' module as it is only used for the JS build.
; 'smallint' is a separate test module.
(modules :standard \ main wasm smallint))
(modules :standard \ main smallint wast))

(executable
(name main)
Expand Down
6 changes: 0 additions & 6 deletions interpreter/meta/jslib/bsconfig.json

This file was deleted.

97 changes: 0 additions & 97 deletions interpreter/meta/jslib/build.sh

This file was deleted.

9 changes: 0 additions & 9 deletions interpreter/meta/jslib/wasm.ml

This file was deleted.

26 changes: 26 additions & 0 deletions interpreter/meta/jslib/wast.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
(* Implements a wrapper library that allows the use of the reference
* interpreter's encode/decode functionality in JavaScript.
*)
open Js_of_ocaml
takikawa marked this conversation as resolved.
Show resolved Hide resolved

let _ =
Js.export "WebAssemblyText"
(object%js (_self)

method encode (s : Js.js_string Js.t) : (Typed_array.arrayBuffer Js.t) =
let def = Parse.string_to_module (Js.to_string s) in
let bs =
match def.Source.it with
| Script.Textual m -> (Encode.encode m)
| Script.Encoded (_, bs) -> bs
| Script.Quoted (_, _) -> failwith "Unsupported" in
let buf = new%js Typed_array.arrayBuffer (String.length bs) in
let u8arr = new%js Typed_array.uint8Array_fromBuffer buf in
String.iteri (fun i c -> Typed_array.set u8arr i (int_of_char c)) bs; buf

method decode (buf : Typed_array.arrayBuffer Js.t) width : (Js.js_string Js.t) =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps by declaring the parameter

(optwidth : int Js.optdef)

and then doing

let width = Js.Optdef.get optwidth (fun () -> 80) in

it is possible to emulate the previous default parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried using JS.optdef, but as far as I can tell, Js_of_ocaml will always export the method such that it gets curried if the full arguments aren't provided instead of calling with undefined.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can achieve that behavior using JS.Unsafe.* functions.
Maybe with val decode = Js.Unsafe.callback (fun bud width -> ...)

let s = Typed_array.String.of_uint8Array (new%js Typed_array.uint8Array_fromBuffer buf) in
let m = Decode.decode "(decode)" s in
Js.string (Sexpr.to_string width (Arrange.module_ m))

end)