fn-literal labels: unforgeable separator, unique named-literal keys; certify pins its oracle JDK - #1082
Merged
Conversation
added 2 commits
September 21, 2026 11:21
…/<name> The anon-fn label joined munge-name(ns) $ munge-name(def) $ n, and the munge itself spells escapes with $, so ns "a$" + def "c" and ns "a" + def "$c" both registered as jfn$a$$$c$0 and the second registration replaced the first — the same forgery #1080 closed for jv$ names. The separator is / now: munge-chars never emits one and the reader never lets one into a namespace or def name. Probing that turned up two more registry collisions. A named inner literal was keyed <name>$jf<n> with the counter per def, so `mapi` in two defs, or in two namespaces, both registered as mapi$jf0, and an image restore of the first closure came back with the second's source. And on the runtime eval path the qualified ns/name branch won for every named fn, not only the def's direct init, and that branch never registered: a closure over a letfn-bound fn dumped from a build and refused from a `jolt run` session. A named literal is now jfn$<ns>/<def>$<n>/<name> on both paths; ns/name stays the direct init's binding, which is what the source registry keys on. A backtrace shows the literal as ns/def/name, the way clojure.stacktrace demunges user$f$mapi__12 (was ns/name, which reads as a var the namespace does not have). *known-procs* maps a name to the site its frame carries so a :local call site records the same name as the callee's frame. The label was never image surface: the restore side uses the record's name only in error text; form, ns and free names travel in the record.
A corpus value is measured on one JDK, and a different one reports its own java.* changes as jolt divergences: JDK 26 raises IllegalStateException where 21 raises NullPointerException on a closed Inflater and reworded the CEN messages, so a local `make ci` on a machine whose clojure launcher found JDK 26 failed certify with 11 NEW rows and nothing wrong. certify.clj only refused an older oracle. profile.edn carries :oracle-jdk (--profile writes the JDK it ran on), certify.clj refuses any other feature version, and make certify hands the launcher the pinned JDK through JAVA_CMD when it is unset and one of the usual installs has it (Homebrew openjdk@N, /usr/lib/jvm/java-N-*, java_home). CI keeps setting JAVA_CMD itself.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #1080, closing the two beads left from it.
jfn$ labels. The anon-fn label used
$as its separator, and the munge spells escapes with$, so nsa$+ defcand nsa+ def$cboth registered asjfn$a$$$c$0. Same forgery #1080 closed forjv$; same fix — the label isjfn$<ns>/<def>$<n>over munge-chars, and/cannot come from an escape or a name.Probing that found two real collisions in the fn-form registry (the image writer's source map):
<name>$jf<n>with the counter per def —(fn mapi …)in two defs, or in two namespaces, both registered asmapi$jf0and an image restore of the first came back with the second's source;ns/namebinding and was never registered at all, so a closure over a letfn-bound fn dumped from a build and refused from ajolt runsession.A named literal is
jfn$<ns>/<def>$<n>/<name>on both paths;ns/namestays the def's direct init only. Backtraces show it asns/def/name(wasns/name, which reads as a var). Not image surface: restore uses the name only in error text.certify.
profile.ednpins:oracle-jdk 21; certify.clj refuses any other JDK (26 changed java.util.zip's exceptions and reported 11 rows as NEW divergences), andmake certifyfinds the pinned JDK forJAVA_CMDwhen it is unset.make ciis green locally with no env var.Both seeds reminted;
make ci141 targets green, selfhost at fixpoint. Filed jolt-ebie for a loaderconf case-13 race seen once under-j8load, unrelated to this change.