Skip to content

Commit

Permalink
Add a tutorial on Juvix module to JS interop via Wasm
Browse files Browse the repository at this point in the history
  • Loading branch information
paulcadman committed Aug 2, 2022
1 parent c496aa7 commit 557ca1d
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 11 deletions.
2 changes: 2 additions & 0 deletions docs/org/SUMMARY.org
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- [[./getting-started/quick-start.md][Quick start]]
- [[./getting-started/dependencies.md][Installing dependencies]]
- [[./examples/README.md][Juvix Examples]]
- [[./tutorials/README.md][Tutorials]]
- [[./tutorials/nodejs-interop.md][NodeJS Interop]]

- [[./language-reference/README.md][Language reference]]
- [[./language-reference/comments.md][Comments]]
Expand Down
1 change: 1 addition & 0 deletions docs/org/tutorials/README.org
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- [[./nodejs-interop.md][NodeJS Interop]]
108 changes: 108 additions & 0 deletions docs/org/tutorials/nodejs-interop.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
* NodeJS Interop

A Juvix module can be compiled to a Wasm module. When a Wasm module is
instantiated by a host, functions from the host can be injected into a Wasm
module and functions from the Wasm module can be called by the host.

In this tutorial you will see how to call host functions in Juvix and call Juvix
functions from the host using the Wasm mechanism.

** The Juvix module

The following Juvix module has two functions.

The function =hostDisplayString= is an =axiom= with no corresponding =compile=
block that implements it. We will inject an implementation for this function
when we instantiate the module from NodeJS.

The function =juvixRender= is a normal Juvix function. We will call this from
NodeJS.

#+begin_src
-- NodeJsInterop.juvix
module NodeJsInterop;

open import Stdlib.Prelude;

axiom hostDisplayString : String → IO;

juvixRender : IO;
juvixRender ≔ hostDisplayString "Hello World from Juvix!";

end;
#+end_src

** Compiling the Juvix module

The Juvix module can be compiled using the following command:

#+begin_src
juvix compile -r standalone NodeJsInterop.juvix
#+end_src

This will create a file containing a Wasm module called =NodeJsInterop.wasm=.

** The NodeJS module

The following NodeJS module demonstrates both calling a Juvix function from
NodeJS and injecting a NodeJS function into a Juvix module.

The NodeJS function =hostDisplayString= is passed to the Wasm module
=NodeJSInterop.wasm= when it is instantiated. After instantiation the Juvix
function =juvixRender= is called.

The functions =ptrToCstr= and =cstrlen= are necessary to convert the =char=
pointer passed from Juvix to a JS =String=.

#+begin_src
-- NodeJSInterop.js
const fs = require('fs');
let wasmModule = null;

function cstrlen(mem, ptr) {
let len = 0;
while (mem[ptr] != 0) {
len++;
ptr++;
}
return len;
}

function ptrToCstr(ptr) {
const wasmMemory = wasmModule.instance.exports.memory.buffer;
const mem = new Uint8Array(wasmMemory);
const len = cstrlen(mem, ptr);
const bytes = new Uint8Array(wasmMemory, ptr, len);
return new TextDecoder().decode(bytes);
}

function hostDisplayString(strPtr) {
const text = ptrToCstr(strPtr);
console.log(text);
}

const wasmBuffer = fs.readFileSync("NodeJsInterop.wasm");
WebAssembly.instantiate(wasmBuffer, {
env: {
hostDisplayString,
}
}).then((w) => {
wasmModule = w;
wasmModule.instance.exports.juvixRender();
});
#+end_src

** Running the Wasm module

Now you should have the files =NodeJsInterop.wasm= and =NodeJsInterop.js= in the
same directory. Run the following command to execute the module:

#+begin_src
node NodeJsInterop.js
#+end_src

You should see the following output:

#+begin_src
Hello World from Juvix!
#+end_src
10 changes: 2 additions & 8 deletions tests/positive/MiniC/ImportExportName/Input.juvix
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ module Input;

open import Stdlib.Prelude;

axiom Void : Type;
axiom hostDisplayString : String → IO;

compile Void {
c ↦ "void";
};

axiom hostDisplayString : String → Void;

juvixRender : Void;
juvixRender : IO;
juvixRender ≔ hostDisplayString "Hello World from Juvix!";

end;
6 changes: 3 additions & 3 deletions tests/positive/MiniC/ImportExportName/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ function cstrlen(mem, ptr) {
return len;
}

function ptrToCstr(wasmMemory, ptr) {
function ptrToCstr(ptr) {
const wasmMemory = wasmModule.instance.exports.memory.buffer;
const mem = new Uint8Array(wasmMemory);
const len = cstrlen(mem, ptr);
const bytes = new Uint8Array(wasmMemory, ptr, len);
return new TextDecoder().decode(bytes);
}

function hostDisplayString(strPtr) {
const wasmMemory = wasmModule.instance.exports.memory.buffer;
const text = ptrToCstr(wasmMemory, strPtr);
const text = ptrToCstr(strPtr);
console.log(text);
}

Expand Down

0 comments on commit 557ca1d

Please sign in to comment.