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.
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 live demo.
Js_of_ocaml_toplevel.JsooTop.initializeBuild your bytecode with debug info and linkall:
ocamlfind ocamlc -g -linkall -package js_of_ocaml-toplevel \
-linkpkg toplevel.ml -o toplevel.byteCompile to JavaScript with the --toplevel flag:
js_of_ocaml --toplevel toplevel.byte -o toplevel.js
Js_of_ocaml_toplevel.JsooTop.initializeBuild your bytecode with debug info and linkall, linking wasm_of_ocaml-compiler.dynlink and js_of_ocaml-toplevel.common:
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.byteCompile to WebAssembly with the --toplevel flag:
wasm_of_ocaml --toplevel toplevel.byte -o toplevel.js
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:
jsoo_listunits -o units.txt stdlib str js_of_ocaml --toplevel --export units.txt toplevel.byte
The --export flag works the same way with wasm_of_ocaml.
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.
js_of_ocaml-compiler.dynlink in your program (initializes dynlink support)Build your bytecode with debug info and linkall:
ocamlfind ocamlc -g -linkall -package dynlink \
-package js_of_ocaml-compiler.dynlink -linkpkg main.ml -o main.byteCompile to JavaScript with the --dynlink flag:
js_of_ocaml --dynlink main.byte -o main.js
wasm_of_ocaml-compiler.dynlink in your program (initializes dynlink support)Build your bytecode with debug info and linkall:
ocamlfind ocamlc -g -linkall -package dynlink \
-package wasm_of_ocaml-compiler.dynlink -linkpkg main.ml -o main.byteCompile to WebAssembly with the --dynlink flag:
wasm_of_ocaml --dynlink main.byte -o main.js
Plugin .cmo files are compiled on the fly to WebAssembly and instantiated at runtime.
(* main.ml *)
let () = Dynlink.loadfile "./plugin.cmo"With js_of_ocaml:
# 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.jsWith wasm_of_ocaml:
# 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.jsBy default, Dynlink.loadfile compiles .cmo and .cma files on the fly. You can also precompile plugins ahead of time for faster loading.
Use js_of_ocaml to compile a .cmo or .cma file to JavaScript:
js_of_ocaml plugin.cmo -o plugin.js js_of_ocaml plugin2.cma -o plugin2.js
Then load the resulting JavaScript file at runtime. In Node.js, use require:
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:
<script src="plugin.js"></script>Use wasm_of_ocaml compile to compile a .cmo to a .wasmo file, or a .cma to a .wasma file:
wasm_of_ocaml compile plugin.cmo -o plugin.wasmo wasm_of_ocaml compile plugin2.cma -o plugin2.wasma
Then load the precompiled file using Wasm_of_ocaml_compiler_dynlink.loadfile:
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.
--toplevel and --dynlink flagsindex — Toplevel API