Skip to content

[new release] js_of_ocaml (8 packages) (6.4.0)#30088

Merged
mseri merged 5 commits into
ocaml:masterfrom
hhugo:release-js_of_ocaml-6.4.0
Jun 23, 2026
Merged

[new release] js_of_ocaml (8 packages) (6.4.0)#30088
mseri merged 5 commits into
ocaml:masterfrom
hhugo:release-js_of_ocaml-6.4.0

Conversation

@hhugo

@hhugo hhugo commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Compiler from OCaml bytecode to JavaScript

CHANGES:

Features/Changes

Bug fixes

CHANGES:

## Features/Changes
* Compiler: initial support for OCaml 5.5.0 (ocsigen/js_of_ocaml#2197, ocsigen/js_of_ocaml#2220)
* Compiler: OxCaml support (ocsigen/js_of_ocaml#2105, ocsigen/js_of_ocaml#2225)
* Compiler: new variable coalescing pass, plus faster variable-naming,
  free-variable and shape-computation passes (ocsigen/js_of_ocaml#2166, ocsigen/js_of_ocaml#2321, ocsigen/js_of_ocaml#2198)
* Compiler: add the `--build-config` and `--apply-build-config` flags (ocsigen/js_of_ocaml#2177)
* Compiler: put more values into global variables (ocsigen/js_of_ocaml#2211)
* Compiler: cosmetic minification in compact mode — `!0`/`!1` booleans,
  drop the leading `0` in `0.x` fractions, normalise exponents
  (`1e+05` → `1e5`), and pick backtick strings when they reduce escapes;
  `--pretty` output is unchanged (ocsigen/js_of_ocaml#1117)
* Wasm: dynlink and toplevel support (ocsigen/js_of_ocaml#2186)
* Compiler/wasm: WASI 0.1 support (ocsigen/js_of_ocaml#1831)
* Wasm_of_ocaml: alternative effect implementation based on the Stack
  Switching proposal (ocsigen/js_of_ocaml#2189)
* Runtime/wasm: implement the legacy `num` library `nat` primitives
  (previously no-op stubs), so arbitrary-precision integers and rationals
  now work as on the JavaScript runtime (ocsigen/js_of_ocaml#2263)
* Runtime/wasm: faster string↔ArrayBuffer copies, faster small-string
  conversions, and optimized bigstring primitives (ocsigen/js_of_ocaml#2124, ocsigen/js_of_ocaml#2144)
* Runtime/wasm: pure-Wasm zstd and BLAKE2b implementations; the runtime
  no longer relies on the JavaScript zstd/BLAKE2 shims to unmarshal
  compressed values or compute Digest.BLAKE512/256/128. This also makes
  both available under the WASI target, which has no JavaScript (ocsigen/js_of_ocaml#2249)
* Runtime: initial support for quickjs-ng (ocsigen/js_of_ocaml#2229)
* Lib: add `Promise` — type-safe bindings to JavaScript promises (even for
  `'a Promise.t Promise.t`), with Lwt interop in `Js_of_ocaml_lwt.Promise`
  (`to_lwt`/`of_lwt`) and Promise-typed `Dom_html` bindings
  (`requestFullscreen`, `requestPointerLock`, `exitFullscreen`,
  `mediaElement.play`, `imageElement.decode`, `Animation.{finished,ready}`).
  Breaking: `mediaElement.play` now returns `unit Promise.t` (ocsigen/js_of_ocaml#2031)
* Lib: add `Fetch` and `Abort` — Fetch API binding with a typed
  `AbortController`/`AbortSignal` primitive for cancellation (ocsigen/js_of_ocaml#596)
* Lib: many additional `Dom_html` bindings and a new `Performance` module
  (ocsigen/js_of_ocaml#2221, ocsigen/js_of_ocaml#2248)
* Lib: add `Console` bindings for `table`, `count`, `countReset` and
  `timeLog` (ocsigen/js_of_ocaml#2350)
* Lib: align `Dom_svg` with SVG 2 — new `graphicsElement`/`geometryElement`
  parents, `style`/`className`/`dataset` merged into `element`, SVG 2
  members, `markerElement` and the full `SVGFE*` filter-primitive family,
  every SVG element is now an event target (ocsigen/js_of_ocaml#519), and `prop`/`readonly_prop`
  fixes. Breaking: drops the SVG 1.1-only `getTransformToElement`;
  `nearestViewportElement`/`farthestViewportElement` are now typed `optdef`
* Lib: implement the popover API (ocsigen/js_of_ocaml#1734)
* Lib: add `Intl.RelativeTimeFormat` (ocsigen/js_of_ocaml#2070)
* Lib: remove dead legacy-browser code from the DOM bindings (IE
  `attachEvent`/`createElement`/`cancelBubble` fallbacks, prefixed
  `requestAnimationFrame`, the Firefox 3.x `File.fileName` property).
  Breaking: drops the obsolete Gecko `MouseScrollEvent`/`_DOMMouseScroll`
  bindings — the `mouseScrollEvent` type, the `MouseScrollEvent`
  `taggedEvent` variant, `CoerceTo.mouseScrollEvent` and
  `Event._DOMMouseScroll` (ocsigen/js_of_ocaml#2350)

## Bug fixes
* Compiler: don't rewrite `x = e + x` into `x += e` for `+` (not
  commutative on strings), which reversed `Filename.concat` operands and
  broke `Filename.temp_file` in whole-program builds (ocsigen/js_of_ocaml#2228)
* Compiler: fix the dead `require()` guard in share_constant (it matched
  "requires"), which could replace `require` string arguments and confuse
  bundlers (ocsigen/js_of_ocaml#2284)
* Compiler: parenthesize `in` in conditional else-branches, arrow concise
  bodies and `for`-initializer yields when re-printing parsed JavaScript,
  which previously emitted output that did not parse (ocsigen/js_of_ocaml#2282)
* Compiler: emit a flat dispatch loop instead of deeply nested labelled
  blocks for many sibling merge targets, avoiding parser "too much
  recursion" overflows (ocsigen/js_of_ocaml#2122)
* Compiler: avoid JS stack overflow on deep mutually-recursive direct-style
  calls under `--effects=double-translation` (ocsigen/js_of_ocaml#2243)
* Compiler: fix reference unboxing (ocsigen/js_of_ocaml#2210), a missing conditional
  simplification (ocsigen/js_of_ocaml#2217), `Js_assign.simpl` (ocsigen/js_of_ocaml#2218), and UGEINT lowering
* Compiler/wasm: fix the int-division return type (ocsigen/js_of_ocaml#2197), preserve the
  physical identity of empty closures (ocsigen/js_of_ocaml#2207), and fix a crash on some
  function calls (ocsigen/js_of_ocaml#2208)
* Runtime: hashing is now consistent across backends — JS strings with code
  points above U+00FF mix two 16-bit units per word, `Hashtbl.hash` of float
  arrays, bigarray tail zero-extension and `float32_hash` normalize like
  native; ASCII/Latin-1 strings are unchanged (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270, ocsigen/js_of_ocaml#2332)
* Runtime/wasm: bring many primitives in line with the JavaScript runtime —
  `caml_wrap_exception` only wraps real `Error`s, `caml_js_meth_call`
  decodes method names as UTF-8, the exception formatter grows its buffer,
  `Sys.is_directory`/`file_exists` follow symlinks, `isatty` returns false
  in browsers, `caml_seek_in` validates the destination, `Array.make`/
  `Obj.new_block` build proper float arrays, `caml_unregister_named_value`
  handles non-head bucket entries, the lexer only moves position memory on
  memory-action transitions, the bytecode-section accessors raise without
  `--toplevel`, format-string `#`/`%+f`/`% f` corner cases, and
  `Condition.wait` is a no-op with each condition variable having a distinct
  identity (ocsigen/js_of_ocaml#2263)
* Runtime: marshalling fixes on both backends — `Marshal.to_buffer` returns
  the byte count; float arrays use `CODE_DOUBLE_ARRAY` (readable by native
  and Wasm); BLOCK32 sizes are decoded with an unsigned shift (≥ 2^21 fields
  no longer truncated) and a block with ≥ 2^22 fields raises instead of
  truncating; `input_value` on a bad object raises `Failure`; bigarray
  deserialization rejects bad dimensions; big-endian double arrays are
  registered in the object table (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270)
* Runtime: filesystem fixes (ocsigen/js_of_ocaml#2270) — the fake device no longer destroys a
  directory renamed into its own subtree, refuses to unlink directories,
  honors `Open_append` (which now implies write access), reports proper Unix
  error codes/syscalls/paths, no longer matches mount points as regexes,
  raises `Sys_error` on cross-device renames, keeps file sizes 64-bit on the
  node backend, and no longer deletes a file renamed onto itself; `flush` on
  a closed channel no longer raises
* Runtime: `O_APPEND`/`Open_append` matches native on both backends — the
  file offset starts at 0 (so `lseek`/`pos_out` report 0 right after opening)
  and every write goes to the end of the file, even after seeking backwards
  (ocsigen/js_of_ocaml#2306)
* Runtime: an empty path raises `ENOENT` on the JavaScript backend like
  native (and the Wasm runtime), so `Unix.stat ""`, `Unix.opendir ""`, etc.
  raise instead of operating on the cwd, and `Sys.file_exists ""` is false
  (ocsigen/js_of_ocaml#2354)
* Runtime: Unix fixes — `Unix.localtime` computes `tm_yday` from the date
  (was off by one during DST) and reaches `Intl` safely; `Unix.close` frees
  the fd-table slot; `Unix.error_message` no longer crashes on unknown codes
  or node < 22; `chmod` raises `Unix_error`; `readdir` includes "." and
  "..", and add `Unix.getegid` / fix the `Unix.getgrgid` export (ocsigen/js_of_ocaml#2263,
  ocsigen/js_of_ocaml#2270, ocsigen/js_of_ocaml#2303, ocsigen/js_of_ocaml#2304)
* Runtime: channel fixes — `in_channel_of_descr`/`out_channel_of_descr`
  allocate fresh channels (they used to share a record and loop on reads);
  refill-hook channels grow their buffer and keep `pos_in`/`pos_out`
  correct; `Sys.command` returns the child exit status; `Sys.getenv` raises
  on prototype names; `Sys.isatty` consults the channel's file (ocsigen/js_of_ocaml#2270, ocsigen/js_of_ocaml#2330)
* Runtime: Str engine fixes — SIMPLEOPT/STAR/PLUS no longer read past the
  end of the string (a trailing negated class could loop forever),
  instruction arguments are no longer masked to 8 bits (regexps with > 256
  pool entries mis-indexed), and a `Str.replace` backreference one past the
  last group raises instead of trapping (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270)
* Runtime: ephemeron/weak fixes — `blit_key`, `blit_data`, `get_data`,
  `get_copy` and `get_data_copy` corrected (no clobbering, copy bytes and
  float arrays, no traps on JS-valued data); data is weakly keyed on the key
  object so key↔data cycles can be collected; `Gc.finalise_last` runs under
  `--effects=cps`; `Gc.counters` returns a plain tuple (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270, ocsigen/js_of_ocaml#2274,
  ocsigen/js_of_ocaml#2279)
* Runtime: numeric fixes — `Digest`/MD5 correct for inputs ≥ 2 GiB;
  `Int64.shift_right` for negatives with shift counts 41–47;
  `Int64.of_string` (ocsigen/js_of_ocaml#2223); `Printf "%#x" 0` prints `0`; float16 bigarray
  NaN comparison; comparing an immediate against a custom block no longer
  throws and orders correctly; `Float.Array.sub`/`append`/`concat` return
  tag-254 arrays; `caml_bigstring_blit_*` treat positions as raw offsets;
  `compare_nat` reads only the common digit length (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270)
* Runtime: float formatting and parsing — the exact decimal expansion is
  printed beyond `toFixed`/`toExponential`'s limits (`%.150e`, `%f` of values
  ≥ 1e21); hex-float literals parse with correct rounding, saturate huge
  exponents instead of wrapping, and reject non-decimal/JavaScript literals;
  `ldexp` rounds once into the subnormal range; and `float_of_string` skips
  all leading whitespace (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270)
* Runtime: `caml_float16_of_double` rounds through `float32` like native, so
  `float16` tie values agree across the js, wasm and native backends; and
  `Unix.getuid`/`geteuid`/`getgid`/`getegid` return the real ids on Node (the
  WASI build keeps `1`) (ocsigen/js_of_ocaml#2280)
* Runtime: Graphics backend fixes (ocsigen/js_of_ocaml#2270) — consistent bottom-left pixel
  origin across all primitives, correct `draw_arc` quadrant, synchronous
  first `draw_image`, `fill_rect` paints the far edges, pie-slice `fill_arc`,
  half-pixel-centred strokes/fills for crisp lines, and round caps/joins,
  matching the native X11 backend
* Runtime: `Domain.spawn` of a raising body now succeeds and `Domain.join`
  re-raises (with the `Finished` payload layout expected per OCaml version),
  instead of corrupting the domain id and termination mutex (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270,
  ocsigen/js_of_ocaml#2302)
* Runtime: `caml_dynlink_open_lib` drops the `mode` argument on OCaml ≥ 5.1
  and returns the filled library-slot index (ocsigen/js_of_ocaml#2270)
* Runtime: misc runtime-review fixes — `Runtime_events.User.register`
  orders `typ`/`tag` correctly and the cursor primitives are renamed
  `caml_ml_runtime_events_*`; `jsoo_effect_not_supported` is provided only
  when effects are disabled; `Blake2.create` truncates an over-long key;
  `OCAMLRUNPARAM` backtrace parsing is last-wins; fix `caml_oo_cache_id`
  (ocsigen/js_of_ocaml#2224) and JS→OCaml string conversion (ocsigen/js_of_ocaml#2230) (ocsigen/js_of_ocaml#2263, ocsigen/js_of_ocaml#2270)
* Lib: binding fixes (ocsigen/js_of_ocaml#2350) — `CSS.Angle` parses integer angles such as
  `"45deg"`; `CSS.Color` rejects empty channels (`"rgb(,,)"`);
  `IntersectionObserver.takeRecords` is wrapped in `Js.t`; `EventSource`
  `onopen`/`onerror` receive a plain `Dom.event`; `Intl.Collator.compare`
  returns `Js.number_t`; `Dom.attr.ownerElement` is typed `element t opt
  readonly_prop`; `Regexp.replace_first` preserves all flags except `g`
  (adds a `flags` accessor)
* Lib: fix method-name mangling — `Typed_array._BYTES_PER_ELEMENT_`,
  `WebGL._MAX_RENDERBUFFER_SIZE_` and `canvasElement.toDataURL_compression`
  resolved to the wrong JavaScript identifiers (ocsigen/js_of_ocaml#2239)
* Lib: defer `Intl.{Collator,DateTimeFormat,...}` member lookups so the
  module no longer throws at load time when `globalThis.Intl` is undefined
  (ocsigen/js_of_ocaml#2229)
* Lib: fix `onbeforeunload` breaking navigation (ocsigen/js_of_ocaml#1436) — the
  `event_listener` return type is now `bool t optdef` (`undefined` means "no
  opinion"), with new `Dom.listener`/`full_listener` and a typed
  `beforeUnloadEvent`
* Lib: fix several `Dom_html` bindings (ocsigen/js_of_ocaml#2221)
* Lib: `Deriving_Json` now round-trips non-finite floats — the writer emits
  `NaN`/`Infinity`/`-Infinity` (matching the reader) instead of OCaml's
  `nan`/`inf`/`-inf`, which the reader rejected (ocsigen/js_of_ocaml#2365)
* Lib: drop a stray `console.log` fired on every event in
  `Lwt_js_events.mousewheel` (ocsigen/js_of_ocaml#2365)
* Lib: `Lwt_file` read functions now fail the thread with an exception on a
  read error or abort instead of raising `assert false` (ocsigen/js_of_ocaml#2365)
* Lib: `Lwt_xmlHttpRequest` frame `content_xml` returns `None` for non-default
  response types (text/json/blob/arraybuffer/document) instead of raising
  `assert false` (ocsigen/js_of_ocaml#2365)
* Lib: `Lwt_js_events.request_animation_frame` is now cancellable — cancelling
  the thread cancels the pending animation-frame callback (ocsigen/js_of_ocaml#2365)
@hhugo
hhugo force-pushed the release-js_of_ocaml-6.4.0 branch from a707459 to b2374ac Compare June 21, 2026 14:58
hhugo and others added 2 commits June 21, 2026 18:56
eliom 12.0.0/12.0.1 do not build against js_of_ocaml 6.4.0: the DOM
bindings changed (getElementsByTagName now returns a Dom.collection,
and Dom_html gained a `listener` value that shadows a local binding).
Fixed in eliom master (ocsigen/eliom#873); cap the existing releases
until a compatible eliom is published.
These releases do not build against js_of_ocaml 6.4.0 with their test
suite enabled: the tests open Js_of_ocaml and use the unqualified module
name Promise, which now resolves to the new Js_of_ocaml.Promise module
(added in 6.4.0) instead of promise_jsoo's own, causing a type error in
Promise.then_. The library itself still installs, but opam install
--with-test (as run by the revdep CI) fails. Fixed since promise_jsoo
0.4.0, which dropped js_of_ocaml-ppx and no longer opens Js_of_ocaml
(0.4.2/0.4.3 build fine); cap the older releases until they are no longer
needed.
@smorimoto
smorimoto force-pushed the release-js_of_ocaml-6.4.0 branch from 6ba0d21 to 0da0436 Compare June 22, 2026 08:05
@hhugo
hhugo force-pushed the release-js_of_ocaml-6.4.0 branch from d7a0d76 to 6ba0d21 Compare June 22, 2026 08:11
hhugo and others added 2 commits June 22, 2026 10:15
6.0.1 declared a continuous "ocaml" {>= "4.08" & < "5.4"} range, dropping
the "< 4.14.3" cutout that 5.9.1 and every earlier version carry. js_of_ocaml
< 6.1 does not build on the OCaml 4.14.3+ patch line (Instruct.compilation_env
lost its ce_rec field, breaking parse_bytecode.ml). Restore the cutout so the
metadata matches 5.9.0; this also fixes the promise_jsoo lower-bounds revdep,
which was selecting 6.0.1 with OCaml 4.14.3.
promise_jsoo_lwt 0.4.x uses Promise.error as a public Ojs.t alias
(introduced in promise_jsoo 0.4.0; in 0.3.x it is `private
Js_of_ocaml.Js.Unsafe.any`). Without a lower bound, opam-repo-ci pairs
promise_jsoo_lwt.0.4.x with promise_jsoo.0.3.1 and the test fails to
compile ([%js.to: int] on a value of type Promise.error). Add the bound
so the revdep test selects a compatible promise_jsoo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mseri

mseri commented Jun 23, 2026

Copy link
Copy Markdown
Member

Is the failure with the data-encoding revdeps new?

@hhugo

hhugo commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

no, they appear at every jsoo release

@mseri

mseri commented Jun 23, 2026

Copy link
Copy Markdown
Member

Thanks

@mseri
mseri merged commit 5ab2263 into ocaml:master Jun 23, 2026
2 of 5 checks passed
@hhugo

hhugo commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author
Root cause Affected packages Notes
Node.js 12 can't parse optional chaining (?.) promise_jsoo 0.1.0–0.3.1, promise_jsoo_lwt 0.4.2/0.4.3 (ubuntu-22.04 tests) js_of_ocaml's runtime uses ?. and requires Node ≥16 (documented). ubuntu-22.04's apt ships node 12.22.9. Generated code fails to parse, not a regression.
Binaryen missing or < 119 all wasm_of_ocaml-compiler.6.4.0, eliom 12.0.x, revdep binaryen.0.19.0 wasm-merge not found / conf-binaryen.1 failed / apt exit 104. Those distros don't package Binaryen ≥119.
Pre-existing test failure data-encoding 0.5–1.0.1 (test_sub_string.ml:35 assertion) Identical across 9 versions ⇒ predates this release, unrelated to jsoo.
Unrelated transitive dep frama_c_codex.1.0-rc4 alt-ergo.1.30 failed to build.
Transient / system pkg js_of_ocaml-ppx_deriving_json (curl failed fetching the 6.4.0 tarball), js_of_ocaml-toplevel (missing libxft-dev) Network blip / distro package, not jsoo.

@hhugo

hhugo commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

@hhugo
hhugo deleted the release-js_of_ocaml-6.4.0 branch June 23, 2026 07:45
@mseri

mseri commented Jun 23, 2026

Copy link
Copy Markdown
Member

Yes, working on it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants