Skip to content

2026.9.29

Latest

Choose a tag to compare

@github-actions github-actions released this 30 Sep 08:21
· 1 commit to develop since this release

MrDocs was presented at CppCon a few weeks ago, and this release comes from what people found when they tried it. It has one large feature and a run of fixes for problems that showed up on real codebases.

The most important feature here is inherit-hidden-friends. When a class gets its operators from a base class as hidden friends, and that base is filtered out, the operators used to disappear from the documentation. That is how mp-units declares every operator on its quantity type. The new option copies or references those operators on the class that inherits them, with the parameter types written in terms of that class. Each function page also says which class it is a hidden friend of. extract-friends is deprecated, since friends are always extracted. A deprecated option now warns with the note from the schema and forwards its value to the option that replaced it.

The fixes are mostly about large projects. A JavaScript extension can now read a corpus of any size. Arrays reach scripts through a proxy, the engine heap grows as needed up to 4 GB, and running out of memory says so instead of crashing. JavaScript loops over the corpus are two to three times faster. A compilation database entry without a file extension no longer crashes. A member declared again in a derived class under other parameter names is no longer listed twice.

The documentation site has a new theme and landing page, and a page that compares the Lua and JavaScript script engines with benchmarks.

🚀 Features

New features and additions

  • ✨ config: Inherit hidden friends of base classes. (fix #1309) 6a984f6 (thanks @mpusz)
  • 💫 ui: Comic design refresh.1 be06d44
  • 🌟 functions: Tag hidden friends on function symbols. (fix #124) ae2ac1b
  • ✨ handlebars: Callable selectors for container helpers. 35fc23c

🐛 Fixes

Bug fixes and error corrections

  • metadata: Same-signature test for redeclared members. 733de65
  • Array proxy writes reach the DOM or throw. (fix #1311) 366dcd5 (thanks @mpusz)
  • JavaScript heap reserved lazily up to 4 GB per context.2 a859ec8
  • A JavaScript extension can read a corpus of any size.3 (Fixes #1294.) d9d4cfa
  • Running out of JavaScript heap says so.4 (Refs #1294.) ee8deca
  • Code generation always reads and writes files as UTF-8.5 (Closes #1310.) 1a0bbac (thanks @gennaroprota)
  • Jerry_port_context_alloc returns the allocated size, not a pointer.6 (Refs #1294.) f1ec499
  • doc:
    • Styled HTML spans keep the inline markup they contain. 7c72084
    • No doc-comment warnings for dependency symbols. 327fea6
  • generators: Implementation-defined bases are omitted from synopses. 334353c
  • handlebars: Underflow-index helpers. 5ba14d2
  • compilation-database: Crash on files without an extension. (fix #1326) 67aff64 (thanks @adyanth)

⚡️ Performance

Performance optimizations and enhancements

  • One proxy handler per JavaScript context.7 6127fb4
  • Engine context is one thread-local load.8 998b918

♻️ Refactor

Code refactoring and restructuring

  • config:
    • Rename extract-implicit-specializations to extract-implicit-base-classes. b3660a7
    • Extract-friends deprecation. 88dbf0f
    • Deprecation notes from the schema. c44cadd
    • Deprecated options forward from the schema. 1535931
  • metadata: Record the base class a copied symbol comes from. f48f6aa
  • finalizers:
    • Deterministic sorted insert. d96cff9
    • Base members finalizer iterates the corpus. b528ff5

📖 Documentation

Documentation updates and improvements

  • Public MRDOCS_* macros in the reference. 9e8b7a4
  • Script engines page and benchmarks.9 adc6242
  • No license page. 3bbc682
  • Design notes are folded. af4269b
  • Html tab for code-block override. fe3841a
  • Start exception condition descriptions with an uppercase letter.10 b45d019
  • As-library examples use indent zero. 93a8d54
  • website:
  • ui:
    • Docs page theme refresh. 9330b21
    • Flowchart diagram theme. 26fba32
    • Config reference preview alignment. 619bbf7
    • Shared syntax palette and gutter token. 6b9d155

📦️ Build

Build system and configuration changes

  • MSan ignores findings that originate inside JerryScript.12 782010f

🚦 Continuous Integration

Changes related to continuous integration

  • Full demos for release tag builds. 7559476
  • No commit cap in release notes. 1e1bab8

Parent release: 2026.9.4 80f517e

  1. Rebuilds the docs theme and the landing page against the Figma comic design, working from the file offline since MCP access to it is denied.

    Layout. Three columns at the frame's widths: 270 nav, 240 contents rail, article filling the rest so the content band it carries always spans it. Reading measure stays 779 at every width. Mobile drops the drawer for an inline nav section plus a
    separate site menu.

    Navbars. Docs and landing are the same bar: 79 tall, 30 inset, 28 item gap, one lockup, the same stroked Iconly Pro sun and moon, the same filled Octocat.

    Dark mode comes from the dark frames (316:335521, 316:232991) rather than being derived: #171C26 navbar, #272E3F page,
    #000 edges, #FF4C4C chips, #A9ABB2 idle rows. Pale light tints saturate on dark, held in --gold-surface and --blue-surface.

    Components. Admonitions at the frame's 10% tints, which lifts title contrast from 5.17 to 10.6-16.7. Tables get a visible
    header, which was #272E3E on a #272E3F ground, i.e. 1.00:1. Search results no longer run off the left edge at any width and
    have dark styling; the theme now owns css/search.css, which the lunr extension defers to.

    Contrast. --ds-color-on-accent was declared light-only in shared/design-system.css, so four consumers inherited --text in dark and drew near-white on gold at ~1.4:1.

    Perf. Chrome icons inline as data URIs so they no longer land after first paint. The search field stays typeable while the index loads.

    Rigs in ~/dev/mrdocs-figtools: audit-spec (206 expectations against the .fig) and audit-overflow, both green. ↩

  2. JerryScript addresses its arena through compressed pointers, and with the 16-bit variant the heap could not exceed 512 KB, so an extension that kept a few thousand symbol objects alive ran out of memory even with lazy arrays.

    JerryScript is now built with JERRY_CPOINTER_32_BIT=ON. Each context reserves just under 4 GB. On POSIX that is mmap(MAP_NORESERVE), so pages cost memory only when written. Windows has no demand-zero pages, so the block is only reserved and a vectored exception handler commits it 1 MB at a time when the engine first touches a page, which gives the same lazy behavior without an upstream change. A failed reservation is halved down to 512 KB. The out-of-memory message names the heap size and leaves through _Exit so static destructors do not call back into the dead engine. The bootstrap patch excludes jerry-port-process.c along with the context port file so the jerry_port_fatal override is portable to MSVC.

    Lua needs no change: it allocates from the system heap and reports out-of-memory as a catchable error. ↩

  3. This fixes a bug for which an extension on a large project ran out of memory before it reached its first line of output. Reading ctx.corpus.symbols was enough to trigger the bug.

    The root cause was that an array was converted to JavaScript element by element, up front. Each element that is an object cost an interpreter object of its own, and the interpreter's heap is a fixed 512 KiB that a build cannot simply enlarge, since 16-bit compressed pointers cap it there. About two thousand symbols filled it.

    An array now reaches JavaScript through a proxy, as an object already did, and an element is converted when a script reads it, so what a script holds has to fit rather than the whole corpus. The proxy wraps a real array, so Array.isArray still answers true and the methods on Array.prototype still work, and a script's own writes go to the wrapped array, where they stay out of the DOM as copying used to keep them.

    Fixes #1294. ↩

  4. A script that required more memory than the interpreter had available caused the process to end with a bare failure code, which is what the engine's default behavior on a fatal condition is.

    Say what happened instead, and where the limit is, so the author of the script can see what they ran into.

    Refs #1294. ↩

  5. The scripts under utils/codegen opened every file without specifying an encoding, so Python used the locale's preferred one: the ANSI codepage on Windows, UTF-8 elsewhere.

    Use UTF-8 everywhere, instead.

    Closes #1310. ↩

  6. jerry_init asks the port for a block to hold the context and the heap, and takes the size of that block from what the call returns. We returned a pointer, instead, so the engine read an address as a length and believed it had a much larger heap than actually available.

    Return the size, instead, as the port function is asked to. The heap is now the size it was allocated, and running out of it ends the way the engine means it to, rather than by corrupting memory.

    Refs #1294. ↩

  7. Every DOM proxy used to build its own handler object and its own set of five or six trap functions, so an element read from ctx.corpus.symbols cost about eight engine objects and 330 bytes, and creating it dominated loops over the corpus. The traps only need the wrapped value, which now lives as a native pointer on the proxy target (their first argument), so a context builds two handlers once, one for objects and one for arrays, and every proxy is just a target plus the Proxy object.

    On the MrDocs corpus (6414 symbols) a count-by-kind loop went from 154 ms to 65 ms, collecting function names from 302 ms to 119 ms, nested property reads from 630 ms to 186 ms, and 102,624 live symbol proxies add 5.4 MB instead of 33.6 MB. ↩

  8. With JERRY_EXTERNAL_CONTEXT every read the engine makes of its own state goes through jerry_port_context_get(), several times per bytecode instruction. Our port answered that with pthread_once plus pthread_getspecific, two library calls per engine field access, which a profile showed as 98% of the samples of any JavaScript loop. The pointer is now a constant-initialized thread_local, one load. On top of that, the JerryScript build force-includes mrdocs-context.h (a compile option set from the patched CMakeLists, no upstream file edited), which defines jerry_port_context_get() as a macro reading that variable, so the engine makes no call at all: the built library references the variable and not the function. An engine built without the header still works through the call, since the variable and the function always agree.

    On the MrDocs corpus a count-by-kind loop went from 62 ms to 29 ms and nested property reads from 182 ms to 116 ms. ↩

  9. A page under Extensions that compares Lua and JavaScript as script engines on the technical side: measured speed on six equivalent transforms over the MrDocs corpus (Lua is 2 to 20 times faster), how symbols reach a script and what a live one costs, how each engine uses memory and what happens on out of memory, and the state of the standard libraries in each engine as probed in the built binary. Ends with guidance on which language to pick. The corpus transforms and Handlebars extensions pages name both languages and link to it.

    tests/benchmarks/ keeps the benchmarks the page quotes so they can be re-measured: the six transforms in both languages over MrDocs's own corpus, a runner that averages 30 runs and prints the table (Markdown or AsciiDoc), and the mrdocs-benchmark-script-engines custom target. Not CTest tests, since timing is machine-dependent. ↩

  10. The @throws conditions in the fixtures were using a mix of uppercase and lowercase for the first letter. This makes things consistent (of course, we leave alone the descriptions that start with a character that is not a letter). ↩

  11. The is_prime example was shown twice. This replaces the second occurrence with a new example. And adds a sixth panel with an example of the @implementationdefined command. ↩

  12. JerryScript 3.0.0's parser reads an uninitialized field of its context while parsing a let declaration, which MemorySanitizer reports from any test that uses let. That is upstream's bug, not ours, so the JerryScript build now gets a sanitizer ignorelist covering jerry-core and jerry-port when compiled with -fsanitize=memory. The code stays instrumented and uninitialized values are still tracked into mrdocs; only reports originating inside those sources are silenced. ↩