From 4148f109a4a210be2ce9e30793f7d67e3186d5a6 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Fri, 15 Jul 2022 18:38:20 -0700 Subject: [PATCH 1/9] [interpreter] Fix Makefile for wast.js build --- interpreter/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index 1783a9c4d8..5f013dbf83 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -112,7 +112,7 @@ _build/$(LIB).cmxa: $(FILES) $(LIB).mllib _tags Makefile $(JSLIB): $(UNOPT) mkdir -p _build/jslib/src cp meta/jslib/* _build/jslib - cp $(DIRS:%=_build/%/*.ml*) meta/jslib/*.ml _build/jslib/src + cp $(wildcard $(DIRS:%=_build/%/*.ml*)) meta/jslib/*.ml _build/jslib/src rm _build/jslib/src/*.ml[^i] (cd _build/jslib; ./build.sh ../../$@) From c7aab2459a18637f8f5205ad1a6a55479118eeb5 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Sat, 16 Jul 2022 12:06:08 -0700 Subject: [PATCH 2/9] [interpreter] Add dune build stanza for wast.js Also add a wast.ml designed to work with Js_of_ocaml --- interpreter/dune | 12 +++++++++++- interpreter/meta/jslib/wast.ml | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 interpreter/meta/jslib/wast.ml diff --git a/interpreter/dune b/interpreter/dune index 6f6dfff3e0..7553d851ab 100644 --- a/interpreter/dune +++ b/interpreter/dune @@ -7,7 +7,7 @@ ; We also need to exclude the 'wasm' module as it overlaps with the library ; name. ; 'smallint' is a separate test module. - (modules :standard \ main wasm smallint)) + (modules :standard \ main wasm smallint wast)) (executable (name main) @@ -23,6 +23,16 @@ (flags (-open Wasm))) +(executable + (name wast) + (modules wast) + (libraries wasm js_of_ocaml) + (flags + (-open Wasm)) + (optional) + (modes js) + (preprocess (pps js_of_ocaml-ppx))) + (subdir text (rule diff --git a/interpreter/meta/jslib/wast.ml b/interpreter/meta/jslib/wast.ml new file mode 100644 index 0000000000..eb85e26ac0 --- /dev/null +++ b/interpreter/meta/jslib/wast.ml @@ -0,0 +1,25 @@ +open Js_of_ocaml + +let decode (buf : Typed_array.arrayBuffer Js.t) width : (Js.js_string Js.t) = + 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)) + +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 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 = decode buf 80 + method decode2 buf width = decode buf width + + end) From b50c462f3396ffb0fee0adc7c57803a17eb3ced8 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Sat, 16 Jul 2022 14:46:05 -0700 Subject: [PATCH 3/9] [interpreter]: modify wast.js target Use dune build to run Js_of_ocaml on wast.ml. --- interpreter/Makefile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index 5f013dbf83..9765895df2 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -109,12 +109,9 @@ _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 $(wildcard $(DIRS:%=_build/%/*.ml*)) meta/jslib/*.ml _build/jslib/src - rm _build/jslib/src/*.ml[^i] - (cd _build/jslib; ./build.sh ../../$@) +$(JSLIB): + dune build wast.bc.js + cp ../_build/default/interpreter/wast.bc.js ./wast.js # Building Windows build file From dbabef2245149dd34562afd122fd766f3976c601 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 18 Jul 2022 17:00:35 -0700 Subject: [PATCH 4/9] [interpreter]: address PR feedback --- interpreter/Makefile | 16 +++-- interpreter/dune | 15 +---- interpreter/meta/jslib/bsconfig.json | 6 -- interpreter/meta/jslib/build.sh | 97 ---------------------------- interpreter/meta/jslib/wasm.ml | 9 --- interpreter/meta/jslib/wast.ml | 17 ++--- 6 files changed, 20 insertions(+), 140 deletions(-) delete mode 100644 interpreter/meta/jslib/bsconfig.json delete mode 100755 interpreter/meta/jslib/build.sh delete mode 100644 interpreter/meta/jslib/wasm.ml diff --git a/interpreter/Makefile b/interpreter/Makefile index 9765895df2..a43c21a950 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -14,7 +14,7 @@ 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 @@ -22,6 +22,7 @@ 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 @@ -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) @@ -108,11 +109,12 @@ _build/$(LIB).cmxa: $(FILES) $(LIB).mllib _tags Makefile # Building JavaScript library -.PHONY: $(JSLIB) -$(JSLIB): - dune build wast.bc.js - cp ../_build/default/interpreter/wast.bc.js ./wast.js +.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 +$(JSLIB).js: $(JSLIB).byte + $(JSO) $(JSLIB).byte # Building Windows build file @@ -178,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 diff --git a/interpreter/dune b/interpreter/dune index 7553d851ab..48274aad99 100644 --- a/interpreter/dune +++ b/interpreter/dune @@ -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 wast)) + (modules :standard \ main smallint wast)) (executable (name main) @@ -23,16 +22,6 @@ (flags (-open Wasm))) -(executable - (name wast) - (modules wast) - (libraries wasm js_of_ocaml) - (flags - (-open Wasm)) - (optional) - (modes js) - (preprocess (pps js_of_ocaml-ppx))) - (subdir text (rule diff --git a/interpreter/meta/jslib/bsconfig.json b/interpreter/meta/jslib/bsconfig.json deleted file mode 100644 index d313bf93d8..0000000000 --- a/interpreter/meta/jslib/bsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "wasm", - "sources": [ - {"dir": "src"}, - ] -} diff --git a/interpreter/meta/jslib/build.sh b/interpreter/meta/jslib/build.sh deleted file mode 100755 index 14471217b5..0000000000 --- a/interpreter/meta/jslib/build.sh +++ /dev/null @@ -1,97 +0,0 @@ -link () { -echo "// DO NOT EDIT. Generated from WebAssembly spec interpreter" -echo " -let WebAssemblyText = (function() { -  let _registry = {__proto__: null}; - function normalize(file) { - return file.split('/').reverse()[0].split('.')[0]; - } -  function require(file) { -    let name = normalize(file); - if (!(name in _registry)) { - throw new Error('missing module: ' + name) -    } else if (typeof _registry[name] === 'function') { -" -if (($LOG == 1)) -then - echo 1>&2 Logging on - echo " - console.log(name); -" -fi -echo " -      let f = _registry[name]; -      _registry[name] = function() { throw new Error('cyclic module: ' + name) }; -      _registry[name] = f(); -    } -    return _registry[name]; -  } -" - -for file in $* -do - echo 1>&2 Including $file - name=`basename $file | sed s/[.]js//g` - echo " - _registry['$name'] = function() { -    let exports = {}; -//////// start of $name.js ////////" - cat $file - echo "//////// end of $name.js //////// -    return exports; -  }; -" -done - -echo " - function binary(bytes) { - let buffer = new ArrayBuffer(bytes.length); - let view = new Uint8Array(buffer); - for (let i = 0; i < bytes.length; ++i) { - view[i] = bytes.charCodeAt(i); - } - return buffer; - } - function bytes(buffer) { - let string = ''; - let view = new Uint8Array(buffer); - for (let i = 0; i < view.length; ++i) { - string += String.fromCodePoint(view[i]); - } - return string; - } -  let Wasm = require('wasm'); - return { - encode(s) { return binary(Wasm.encode(s)) }, - decode(b, w = 80) { return Wasm.decode(bytes(b), w) } - }; -})(); - -" -} - -echo 1>&2 ==== Preparing ==== -npm link bs-platform - -echo 1>&2 ==== Compiling ==== -bsb || exit 1 - -echo 1>&2 ==== Linking full version ==== -LOG=1 -FILES='node_modules/bs-platform/lib/js/*.js lib/js/src/*.js' -link $FILES >temp.js || exit 1 - -echo 1>&2 ==== Running for dependencies ==== -node temp.js | tee temp.log || exit 1 - -echo 1>&2 ==== Linking stripped version ==== -used='' -for file in `ls $FILES` -do - if grep -q `basename $file | sed s/.js//g` temp.log - then - used="$used $file" - fi -done -LOG=0 -link $used >$1 || exit 1 diff --git a/interpreter/meta/jslib/wasm.ml b/interpreter/meta/jslib/wasm.ml deleted file mode 100644 index 915fdafb08..0000000000 --- a/interpreter/meta/jslib/wasm.ml +++ /dev/null @@ -1,9 +0,0 @@ -let encode s = - let def = Parse.string_to_module s in - match def.Source.it with - | Script.Textual m -> Encode.encode m - | Script.Encoded (_, bs) -> bs - -let decode s width = - let m = Decode.decode "(decode)" s in - Sexpr.to_string width (Arrange.module_ m) diff --git a/interpreter/meta/jslib/wast.ml b/interpreter/meta/jslib/wast.ml index eb85e26ac0..9af04f9189 100644 --- a/interpreter/meta/jslib/wast.ml +++ b/interpreter/meta/jslib/wast.ml @@ -1,10 +1,8 @@ +(* Implements a wrapper library that allows the use of the reference + * interpreter's encode/decode functionality in JavaScript. + *) open Js_of_ocaml -let decode (buf : Typed_array.arrayBuffer Js.t) width : (Js.js_string Js.t) = - 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)) - let _ = Js.export "WebAssemblyText" (object%js (_self) @@ -14,12 +12,15 @@ let _ = let bs = match def.Source.it with | Script.Textual m -> (Encode.encode m) - | Script.Encoded (_, bs) -> bs in + | 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 = decode buf 80 - method decode2 buf width = decode buf width + method decode (buf : Typed_array.arrayBuffer Js.t) width : (Js.js_string Js.t) = + 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) From 35a7bbb4dbb77eabab1737ac12b736807e866768 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 18 Jul 2022 17:19:01 -0700 Subject: [PATCH 5/9] [interpreter]: adjust wast.js documentation --- interpreter/Makefile | 2 +- interpreter/README.md | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index a43c21a950..e7dfc33b48 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -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. diff --git a/interpreter/README.md b/interpreter/README.md index 6f55c1f3f4..6b9603e44b 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -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 @@ -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. @@ -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")); +``` + +Or using `load` from a JavaScript shell: + +``` +load("./wast.js"); +let binary = WebAssemblyText.encode("(module")); +``` + ## S-Expression Syntax From 4a4fc5ec8f10c4e7967a6e73eb597f7b862ab069 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 18 Jul 2022 17:24:43 -0700 Subject: [PATCH 6/9] [interpreter]: create GH action for wast.js --- .github/workflows/main.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4a5527ca29..9a7a111a15 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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: From 54bc584fcce54d4e55b24d3f12c6d289e2d99202 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 18 Jul 2022 17:54:48 -0700 Subject: [PATCH 7/9] [interpreter]: minor fix in wast.js example --- interpreter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/README.md b/interpreter/README.md index 6b9603e44b..26ddba520c 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -154,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))) From 94fee80148c4fbdf168eae489482ae6afc8153e9 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Tue, 19 Jul 2022 11:24:37 -0700 Subject: [PATCH 8/9] [interpreter]: address additional feedback --- interpreter/Makefile | 9 ++++++--- interpreter/README.md | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index e7dfc33b48..191cde5f33 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -109,12 +109,15 @@ _build/$(LIB).cmxa: $(FILES) $(LIB).mllib _tags Makefile # Building JavaScript library +JSLIB_DIR = meta/jslib +JSLIB_FLAGS = -use-ocamlfind -pkg js_of_ocaml -pkg js_of_ocaml-ppx + .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 +$(JSLIB).byte: $(JSLIB_DIR)/$(JSLIB).ml + $(OCBA) -I $(JSLIB_DIR) $(JSLIB_FLAGS) $@ $(JSLIB).js: $(JSLIB).byte - $(JSO) $(JSLIB).byte + $(JSO) $< # Building Windows build file diff --git a/interpreter/README.md b/interpreter/README.md index 26ddba520c..11258ae62e 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -167,14 +167,14 @@ Depending on how you load the library, the object may be accessed in different w ``` let wast = require("./wast.js"); -let binary = wast.WebAssemblyText.encode("(module")); +let binary = wast.WebAssemblyText.encode("(module)"); ``` Or using `load` from a JavaScript shell: ``` load("./wast.js"); -let binary = WebAssemblyText.encode("(module")); +let binary = WebAssemblyText.encode("(module)"); ``` From a443bedfcd0ee59dee19f8c3e3d10050dfd883ec Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Tue, 19 Jul 2022 11:31:58 -0700 Subject: [PATCH 9/9] [interpreter]: minor refactoring --- interpreter/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index 191cde5f33..7820e64282 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -110,11 +110,11 @@ _build/$(LIB).cmxa: $(FILES) $(LIB).mllib _tags Makefile # Building JavaScript library JSLIB_DIR = meta/jslib -JSLIB_FLAGS = -use-ocamlfind -pkg js_of_ocaml -pkg js_of_ocaml-ppx +JSLIB_FLAGS = -I $(JSLIB_DIR) -use-ocamlfind -pkg js_of_ocaml -pkg js_of_ocaml-ppx .INTERMEDIATE: $(JSLIB).byte $(JSLIB).byte: $(JSLIB_DIR)/$(JSLIB).ml - $(OCBA) -I $(JSLIB_DIR) $(JSLIB_FLAGS) $@ + $(OCBA) $(JSLIB_FLAGS) $@ $(JSLIB).js: $(JSLIB).byte $(JSO) $<