{0 Toplevel and Dynlink}

This page explains how to build an OCaml toplevel (REPL) that runs in the browser,
and how to use dynamic linking to load bytecode at runtime.

Both js_of_ocaml and wasm_of_ocaml support these features.

{1:toplevel Building a toplevel}

A toplevel is an interactive OCaml environment (like [ocaml] or [utop]).
Js_of_ocaml and wasm_of_ocaml can compile a toplevel to JavaScript or WebAssembly,
allowing it to run in the browser.
See the {{:files/toplevel/index.html}live demo}.

{2 With js_of_ocaml}

+ Initialize the toplevel in your OCaml code using
  {!Js_of_ocaml_toplevel.JsooTop.initialize}

+ Build your bytecode with debug info and linkall:
{v
ocamlfind ocamlc -g -linkall -package js_of_ocaml-toplevel \
    -linkpkg toplevel.ml -o toplevel.byte
v}

+ Compile to JavaScript with the [--toplevel] flag:
{v
js_of_ocaml --toplevel toplevel.byte -o toplevel.js
v}

{2 With wasm_of_ocaml}

+ Initialize the toplevel in your OCaml code using
  {!Js_of_ocaml_toplevel.JsooTop.initialize}

+ Build your bytecode with debug info and linkall, linking
  [wasm_of_ocaml-compiler.dynlink] and [js_of_ocaml-toplevel.common]:
{v
ocamlfind ocamlc -g -linkall -package wasm_of_ocaml-compiler.dynlink \
    -package js_of_ocaml-toplevel.common -package compiler-libs.toplevel \
    -linkpkg toplevel.ml -o toplevel.byte
v}

+ Compile to WebAssembly with the [--toplevel] flag:
{v
wasm_of_ocaml --toplevel toplevel.byte -o toplevel.js
v}

{2 Limiting available modules}

By default, all linked modules are available in the toplevel. To limit this,
use [--export FILE] where [FILE] lists compilation unit names (one per line).

The [jsoo_listunits] tool generates this list from findlib libraries:

{v
jsoo_listunits -o units.txt stdlib str
js_of_ocaml --toplevel --export units.txt toplevel.byte
v}

The [--export] flag works the same way with wasm_of_ocaml.

{1:dynlink Using the Dynlink library}

OCaml's [Dynlink] module lets you load bytecode files at runtime. This works
in both js_of_ocaml and wasm_of_ocaml with some setup.

{2 With js_of_ocaml}

+ Link [js_of_ocaml-compiler.dynlink] in your program (initializes dynlink support)

+ Build your bytecode with debug info and linkall:
{v
ocamlfind ocamlc -g -linkall -package dynlink \
    -package js_of_ocaml-compiler.dynlink -linkpkg main.ml -o main.byte
v}

+ Compile to JavaScript with the [--dynlink] flag:
{v
js_of_ocaml --dynlink main.byte -o main.js
v}

{2 With wasm_of_ocaml}

+ Link [wasm_of_ocaml-compiler.dynlink] in your program (initializes dynlink support)

+ Build your bytecode with debug info and linkall:
{v
ocamlfind ocamlc -g -linkall -package dynlink \
    -package wasm_of_ocaml-compiler.dynlink -linkpkg main.ml -o main.byte
v}

+ Compile to WebAssembly with the [--dynlink] flag:
{v
wasm_of_ocaml --dynlink main.byte -o main.js
v}

Plugin [.cmo] files are compiled on the fly to WebAssembly and instantiated at runtime.

{2 Example}

{@ocaml[
(* main.ml *)
let () = Dynlink.loadfile "./plugin.cmo"
]}

With js_of_ocaml:
{v
# Compile main program
ocamlfind ocamlc -g -linkall -package dynlink \
    -package js_of_ocaml-compiler.dynlink -linkpkg main.ml -o main.byte
js_of_ocaml --dynlink main.byte -o main.js

# Compile plugin
ocamlfind ocamlc -c plugin.ml

# Run
node ./main.js
v}

With wasm_of_ocaml:
{v
# Compile main program
ocamlfind ocamlc -g -linkall -package dynlink \
    -package wasm_of_ocaml-compiler.dynlink -linkpkg main.ml -o main.byte
wasm_of_ocaml --dynlink main.byte -o main.js

# Compile plugin
ocamlfind ocamlc -c plugin.ml

# Run
node ./main.js
v}

{1:precompiled Loading precompiled files}

By default, [Dynlink.loadfile] compiles [.cmo] and [.cma] files on
the fly. You can also precompile plugins ahead of time for faster loading.

{2 With js_of_ocaml}

Use [js_of_ocaml] to compile a [.cmo] or [.cma] file to JavaScript:

{v
js_of_ocaml plugin.cmo -o plugin.js
js_of_ocaml plugin2.cma -o plugin2.js
v}

Then load the resulting JavaScript file at runtime. In Node.js, use
[require]:

{@ocaml[
let require s =
  Js_of_ocaml.Js.Unsafe.fun_call
    (Js_of_ocaml.Js.Unsafe.js_expr "require")
    [| Js_of_ocaml.Js.Unsafe.inject (Js_of_ocaml.Js.string s) |]

let () = require "./plugin.js"
]}

In a browser, use a [<script>] tag:

{@html[
<script src="plugin.js"></script>
]}

{2 With wasm_of_ocaml}

Use [wasm_of_ocaml compile] to compile a [.cmo] to a [.wasmo] file,
or a [.cma] to a [.wasma] file:

{v
wasm_of_ocaml compile plugin.cmo -o plugin.wasmo
wasm_of_ocaml compile plugin2.cma -o plugin2.wasma
v}

Then load the precompiled file using
{!Wasm_of_ocaml_compiler_dynlink.loadfile}:

{@ocaml[
let () = Wasm_of_ocaml_compiler_dynlink.loadfile "./plugin.wasmo"
]}

This skips the on-the-fly bytecode-to-Wasm compilation step, which can
significantly reduce loading time for large plugins.

{1 See also}

- {{!page-"options"}Command-line options} — The [--toplevel] and [--dynlink] flags
- {{!page-"compilation-modes"}Compilation modes} — Whole program vs separate compilation
- {!index} — Toplevel API
