Repository navigation
Retarget qj to jq 1.8.2 - #27
Merged
Merged
Conversation
The update script no longer fetches utf8-truncate.jq, which jq 1.8.2 removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- jv_dump_string_trunc's new cut (whole characters, then ... and the closing delimiter) and 30-byte buffers in every error that dumps a value; jv_get's errors are always `Cannot index <kind> with <kind> (<key>)`. - jv_equal, jv_cmp, jv_contains and object * stop past depth 10000 (Equality check / Comparison / Containment check / Object merge too deep), checked where jq checks them: ==, !=, <, sort, group_by, unique, min/max, bsearch, array -, indices, contains, IN. - getpath, setpath and delpaths refuse paths longer than 10000; delpaths skips NaN array indices and fails jq's assertion when sorting its paths is too deep. - MAX_PRINT_DEPTH is 10000; the printer continues with an explicit stack past 64 levels. jv_free no longer recurses, so drops are no longer checked against compat mode's free model. - Strings of INT_MAX - 8 bytes or more: Repeat string result too long, String too long; literals longer than DEC_MAX_DIGITS are invalid. - tonumber and toboolean reject strings with a NUL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- @urid decodes every byte (NUL included) and validates the whole result as UTF-8. - _strindices checks both arguments' types instead of failing an assertion. - strflocaltime reports localtime's error instead of failing an assertion; set_tm_yday's assertion is at builtin.c line 1621. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
from_entries is `map(...) | add // {}` (no label any more, so the native
stops advancing the label counter) and rtrimstr cuts
`.[:length - ($right | length)]`. The native's iteration error dumps
30 bytes too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-measured against jq 1.8.2's release binaries on macOS 27, macOS 26 and Linux x86-64, by padding the environment at a fixed limit until jq no longer answers, shape by shape against 1.8.1: * jv_free frees iteratively now, so Site::Free and the native-drop limit go. * jv_equal and jv_cmp are separate sites: gcc gives jvp_equal 160 bytes a level on Linux, against jvp_cmp's 144 (both 128 on macOS). * jv_object_merge_recursive is 16 bytes a level more on both (128 / 144). * Linux's shared value base is 3,216 (sort); the module chain, compile and macOS's regex bases move by 16; --run-tests holds 32,800 bytes on macOS (12,776 on Linux), with a floor of 44,368 (23,880). * The depth walks stop where jq 1.8.2 gives up, past 10,000 levels. At a 512 KB stack compat-mode qj dies 2 to 9 levels short of jq at every value site. The compat_mode tests over values run at a small stack, where the recursions can still overflow before jq's limit stops them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n 1.8.2 die() prints "online docs at" with one space. A -f program file holding a NUL byte is refused with "program file contains NUL bytes" (exit 2) instead of being cut there. Compat mode's version and help say 1.8.2, and VERSION_DIR is 1.8.2. The JSON parser's tokenadd now fails with "Token too long" past 2^30 - 1 bytes, as jq 1.8.2's does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
linker.c: a module is recorded once it parses, before its own imports, so importing one still loading reports "circular import of <path>" (in both modes; compat mode no longer dies of the old recursion). A module that doesn't parse isn't recorded, so importing it again parses it again. A module path with a NUL byte is refused, and import paths keep their NUL bytes (modulemeta's deps show them). Libraries are linked in that order, each before the ones it imports, where jq's block_drop_unreferenced frees definitions a kept one still calls. With QJ_JQ_COMPAT=1 qj follows jq's walk and dies as jq does when it compiles such a call (expand_call_arglist's assert) or reads a freed data import (stack_push's); by default it drops only what nothing kept reaches, which is what jq drops whenever it doesn't abort. compile.c: at most 4095 subfunctions and parameters per function. The rest of compile now runs in jq's order (each subfunction's parameters assigned just before it is compiled, code emitted after the subfunctions and only without errors), so compat mode also dies where jq does after these errors. check_object_key's buffer is 30 bytes. Corpus: modules.toml calls at most two imports deep (gen_modules.py), the aborts are compat_mode.toml cases, the limits are in jq182_cli.toml. tests/jq_compile/disasm.jsonl is re-recorded with jq 1.8.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… NUL cut jq 1.8.2's read_more fgets at most 4091 bytes, then freads what the UTF-8 sequence a full chunk ends in is missing (newlines included: one there ends no chunk and isn't counted, and with -R the line goes on past it unless it is the chunk's last byte). A chunk without a newline is no longer cut at its first NUL, so NUL bytes reach jq's parser. Both readers do this: the CLI's util.c port, and src/io's reader, which now keeps an anchor (the start of the chunk its position is in) instead of the line start, walks chunks where a line runs past 4091 bytes, tracks the last complete chunk of a stream as it reads, and gives the parallel engine where a job's first chunk ends; a job's line whose newline fread swallows is read by the reader. The util.c oracle in src/io/tests follows 1.8.2, and the generated inputs put UTF-8 sequences, cut short or not, at the 4091-byte boundary. Corpus: cli_io.toml (gen_cli_io.py) gets inputs around the 4091-byte chunks and NULs; jq182_cli.toml gets jv_load_file's 4096-byte reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tests --run-tests runs every file it is given, each with the --skip/--take given before it (stdin when there is none); --skip or --take without a count says so and exits 1 instead of crashing. An expected result that doesn't survive printing and parsing back (a NaN) fails the test with a message instead of an assert(). After the start-state tests come jq 1.8.2's jq_compile_args, recompile and exhaust-and-reuse subtests, printing their lines. Test files are still read in 4095-byte lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The value and builtin fixtures (gen.py, b1_gen.py) and the parser cases (jq_lang_parse.rs) are re-recorded with the jq 1.8.2 binary, and the tests that run jq check for jq-1.8.2. What changed in the data is jq 1.8.2's value-layer and builtin changes (index error messages, printing depth, NULs reaching the parser) and the new upstream test programs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also the baseline file's header says jq 1.8.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
advance counts newlines while it walks the chunks of a long range; a range with no newline, or a raw chunk, is walked by length alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both scoreboards: 40,342 cases, compat mode strict on all of them and the default strict on all but qj's own help and version text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README, CLAUDE.md, the port plan, LIMITATIONS and COMPATIBILITY say 1.8.2 where they say what qj matches. COMPATIBILITY.md has the macOS scoreboard (40,342 cases, compat mode strict on all), what 1.8.2 changed in the crash table (the depth limits, iterative jv_free, circular imports, the linker's use-after-free on module chains and the closure limit), the re-measured stack models with what moved, the --run-tests needs, and the undefined behaviour re-run against 1.8.2 (jv_dels' double free is fixed). The Linux numbers and the Release check's platforms are still 1.8.1's until CI runs them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_strindices was in SEQUENTIAL_BUILTINS because jq 1.8.1's f_string_indexes
passed any argument to jv_string_indexes, whose assert() aborted the process
on a non-string; on macOS abort() flushes the output produced so far, so the
records before the abort had to have been processed in order. jq 1.8.2 checks
both kinds in f_string_indexes first and raises ordinary type errors ("cannot
be searched, as it is not a string", "is not a string"), and qj ports that in
both default and compat mode: compat mode has no _strindices abort left to
reproduce. The builtin keeps no state across records, so records using it are
independent and can go through the parallel engine like any other.
strptime stays sequential: jq 1.8.2 still fails set_tm_yday's assert() on
macOS.
The new ndjson test runs _strindices with type errors over 20,000 records on
four threads and compares stdout, stderr and the exit code with jq.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
STACK_BYTES' comment counted MAX_PRINT_DEPTH among the jq limits that bound qj's deepest recursions. In jq 1.8.2 it is 10,000 (it was 256), and qj's printer only recurses for the first 64 levels before it continues with an explicit stack, so printing belongs with the recursions that turn into loops. gen.py's deep print documents sit around 1.8.1's limit of 256; say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comments described what jq 1.8.1 did: overflow its stack freeing, comparing, merging and walking paths of deep values, and loop forever on a NaN path element. jq 1.8.2 frees without recursing, stops the other value recursions at 10,000 levels with "Path too deep", "Object merge too deep", "Containment check too deep", "Equality check too deep" and "Comparison too deep", skips NaN keys in delpaths, and raises the slice error where it freed the key twice. Each comment now says what the case checks against jq 1.8.2 (checked with the 1.8.2 binary), keeping the 1.8.1 history where it explains a case's name or depth. Only comments and one `note` (which the harness ignores) change: names, args, files, env and mem_mb stay, so ids and fingerprints do too, and both scoreboards stay at their baselines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- value/mod.rs listed two deviations from jq that jq 1.8.2 no longer has: delpaths with a NaN path element (1.8.2 skips it, as qj does) and a malformed slice key in delpaths on an array (1.8.2 raises the slice error instead of freeing the key twice). Only "Object too big" is left. - The live value test kept those two delpaths shapes out because jq 1.8.1 hung or corrupted its heap on them; with jq 1.8.2 they run and match. - platform/mod.rs's examples of jq's assert() failures used two that 1.8.2 removed: strflocaltime of an unrepresentable time (now an error) and _strindices of a non-string (now a type error). They use strptime's set_tm_yday assert (macOS) and jv_array_length's (delpaths after a failed sort) instead, both still reachable in jq 1.8.2. - The undefined-behaviour scripts were written against jq 1.8.1; note that 1.8.2 fixed jv_dels' double free, and the other two remain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jq_diff_windows.py refused any jq but jq-1.8.1; the jq_diff cases and baselines are jq 1.8.2's now, so it requires jq-1.8.2. The known-mismatch list's header and the workflow and Windows script comments name the version they run against (mise.toml already installs 1.8.2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What qj ports, and what tests and fixtures take as their expectation, is jq 1.8.2 now. Each reference was checked against 1.8.2 before it changed: - Port headers (main.c, util.c, execute.c, exec_stack.h, compile.c, parser.y, lexer.l, locfile.c, jv*.c, builtin.c, libm.h): the ports follow 1.8.2. builtin.jq is vendored verbatim from 1.8.2; Oniguruma (6.9.10) and libm.h are unchanged; the function list is still 64 CFUNCs and 11 BINOPS; there is still no @base32. strptime.c cites util.c's lines in 1.8.2 (448-1258; the code is identical). - Test expectations: the platform cases (gen_cases.py, regenerated with 1.8.2: no change), the libm corpus (4,415 rows) and the regex corpus (650 rows), the binding/scoping disassembly in jq_compile.rs and the closed descriptor combinations in compat_mode.rs were all rerun against the 1.8.2 binary and match. The strptime abort cases still abort in 1.8.2. - Generators: gen_parser_tables.py reads target/jq-src/jq-1.8.2 and gen_builtin_matrix.py requires jq-1.8.2; both were rerun, and only the version in their output's header changed. The fuzzers require jq-1.8.2. - Corpus .test/.toml files: comment lines only, so both scoreboards stay at their baselines with the same cases. Kept as they are: true history (jq 1.8.1 hung on NaN keys in delpaths, the undefined-behaviour scripts' subject, build.rs's measurement against 1.8.1's binary), the version pair in the jq_diff cache's invalidation test, and the "ICU 1.8.1" license name in LICENSE-jq. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ls on Windows jq 1.8.2 checks localtime's result in strflocaltime, so the only platform abort left is set_tm_yday's on macOS, and the Windows smoke test's 100,000 levels are past what jq 1.8.2 compares (Equality check too deep). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the ci/jq-1.8.2 run's jq-diff-linux artifact: 40,366 cases, the compat board strict on all but the data import in a chain of modules, which jq 1.8.2 aborts on while compiling on Linux (to be modelled). On Windows jq.exe now compiles cli_basics' deep-binops, and aborts on modules' data import in a chain, where qj answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jq 1.8.2 frees the global of a data import two modules below the program when only an unwalked definition refers to it. With glibc, compiling that reference fails nesting_level's assert() (compile.c:1091) before anything runs; with Apple's libc the reference compiles and stack_push's assert() fails when it is read. Compat mode now follows glibc on Linux and keeps the macOS behaviour elsewhere (musl is unmeasured). Corpus: modules.toml's data-import-in-a-chain-unused is macOS-only (jq aborts on Linux), and gets non-aborting neighbours; compat_mode.toml's data-import aborts are split into macOS and Linux cases. The macOS baselines record the new cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the ci/jq-1.8.2 Checks run: 40,372 cases on Linux, the compat board strict on all of them and the default on all but qj's help and version text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Intel macOS jq 1.8.2's nesting_level finds a freed data import's global compiled into another function on Linux with musl as with glibc (compile.c:1091), and never compiled on Intel macOS (compile.c:1086); only arm64 macOS reaches stack_push. Compat mode follows each. modules.toml drops data-import-in-a-chain-unused: whether jq aborts on it depends on the platform, and compat_mode.toml covers it per platform. The Windows known-failures list and the macOS baselines drop its entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Linux aarch64 and macOS x86-64 are strict on the compat board (40,372 and 40,347 cases); on musl one chain of modules dies of SIGSEGV in jq where compat mode aborts as on glibc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
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.
qj now matches jq 1.8.2, the patch release of 2026-06-20, instead of 1.8.1. Most of 1.8.2's 86 commits are security fixes, and many of them change behavior; qj follows all of them.
Conformance (jq_diff against jq 1.8.2):
QJ_JQ_COMPAT=1(strict)The default board's 7 misses are qj's own help and version text, as before. FreeBSD and NetBSD match except for those 7. musl adds its known stdout/stderr ordering differences and one module crash where its heap differs. Windows matches
jq.exeexcept the cases in.github/jq_diff_windows_known.txt.What 1.8.2 changed that qj now does:
Cannot index X with Y (key), and 30-byte truncation of values in errors that keeps a closing quote or bracket.... too deeperrors. Freeing values no longer recurses, and the printer's depth limit is 10,000.tonumber/tobooleanreject NUL bytes,@uri/@uridare rewritten,_strindicesraises type errors,rtrimstr("")andfrom_entriesare fixed, and string length bounds are tightened.-frejects program files with NUL bytes.die()'s text changed.--run-teststakes several files and checks--skip/--take.jq 1.8.2's own crashes, reproduced in compat mode only (the default keeps the sane answer):
assert(). It aborts the same way in every run, but how differs by platform: macOS arm64, macOS x86-64, glibc, musl and Windows each fail differently, and compat mode follows each measured platform.Compat mode:
delpathswithnan,jv_freeoverflows, andjv_dels' double free.Also:
builtin.jqand test modules._strindicesno longer forces sequential processing.Not re-run for 1.8.2: the 121-program small-stack corpus and the Linux side of the undefined-behaviour corpus. COMPATIBILITY.md says so.
🤖 Generated with Claude Code