fix(hir+runtime+parity): #1273 #1275 #1276 #1278 close v0.5.1019 parity bugs - #1282
Merged
Merged
Conversation
…ty bugs - #1273 Buffer.from(str, encodingVar): HIR lowering routed any non-string-literal second arg to BufferFromArrayBuffer, which fed a string encoding into the byte-offset path. New rule: ArrayBuffer form only when 3+ args or a Number-literal second arg; otherwise the encoding form (runtime helper js_encoding_tag_from_value already handles runtime-string encodings). - #1275 console.log UTF-8 mojibake: js_util_format pushed each format-string byte as `byte as char`, casting UTF-8 bytes through Latin-1. Switched to emitting literal segments as `&str` slices so multi-byte codepoints ("for…of", "中") survive the format pass. - #1276 console.table spurious 'Values' column: the array-of-arrays branch always appended a Values column; Node skips it when every row is an array. Now gated on the actual mix of row types. - #1278 console.trace / Error.stack frame format: categorical gap (needs source-position retention through the lowering pipeline). Added test_gap_console_methods to test-parity/known_failures.json with category: gap-categorical and #1278 as the tracking issue. #1274 (EventEmitter numeric/object args) and #1277 (console.time precision) were re-verified against current main and pass byte-for-byte vs Node; #1274 was fixed by a recent commit and #1277 was a misdiagnosis (the 1000x gap in the issue's repro is AOT-vs-interpreted speedup on a trivial loop, not a clock-unit bug — Perry's Instant::now path is correct).
This was referenced May 21, 2026
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.
Summary
Six parity issues surfaced by the v0.5.1019 CI gate (run 26236443275). Four are code fixes, two were re-triaged against current main:
crates/perry-hir/src/lower/expr_call/{native_module,module_static}.rscrates/perry-runtime/src/builtins/formatting.rscrates/perry-runtime/src/builtins/table.rstest-parity/known_failures.json#1273 —
Buffer.from(str, encodingVar)returned emptyHIR lowering for
Buffer.from(data, X)routed any non-string-literal second arg toBufferFromArrayBuffer, soBuffer.from("cGVycnk=", encVar)fed the encoding string into the byte-offset path and produced an empty buffer. New disambiguation: ArrayBuffer form only when there are 3+ args or the second arg is aNumberliteral; otherwise the encoding form. The encoding form already runtime-dispatches ondata's actual type viajs_buffer_from_value, andjs_encoding_tag_from_valuehandles runtime-string encodings.Buffer.from(ab, 1, 2)and all otherBuffer.fromshapes intest_parity_bufferremain byte-for-byte vs Node.#1275 —
console.log("for…of:", …)rendered mojibake (forâ¦of)js_util_formatwalked the format string byte-by-byte and pushed each byte asbyte as char, which casts a UTF-8 byte to a Latin-1 codepoint. The 3-byte UTF-8 sequence for…(E2 80 A6) came out asâ\u{80}¦→â¦on the terminal. Bug only triggered on the multi-arg / format-string path; single-stringconsole.log("…")already used a different path and was correct, which matches the issue's repro.Fix: emit literal-text segments as
&strslices between%specifiers (and flush the trailing segment after the loop) so multi-byte codepoints survive intact.#1276 —
console.table([[1,2],[3,4]])had a spuriousValuescolumnNode skips the trailing
Valuescolumn when every row is an array; Perry's array-of-arrays branch was appending it unconditionally. Now gated on the actual mix of row types — array-of-arrays gets noValuescol, mixed array/primitive rows still get one (matches Node'sconsole.table([Symbol(), 5, [10]])shape).#1278 — stack-trace format diverges from Node (categorical)
Perry prints native frame addresses (
0: __mh_execute_header) where Node printsat <fn> (file:///…:line:col). Per the issue itself, this is a genuinely categorical gap that needs source-position retention through HIR → transform → codegen + a native-frame → TS-source mapper. Addedtest_gap_console_methodstotest-parity/known_failures.jsonwithcategory: \"gap-categorical\"and #1278 as the tracking issue; the entry also documents why #1276 (already fixed in this PR) and #1277 (misdiagnosis) are not gating.#1274 — EventEmitter numeric/object args (already passes)
Re-ran
test_express_mountandtest_issue_850_eventemitteragainst current main: both match Node byte-for-byte. The numeric/object payload arrives correctly at the listener. Likely fixed by one of the recent jsruntime / class-method-dispatch landings between the issue filing and now.#1277 —
console.timeprecision (not a bug)The issue's repro is a 1000-iter empty loop:
On compiled Perry the loop is dead-code-eliminated and only the two
console.time*calls remain (microseconds). On interpreted Node the loop runs at ~1ms. Verified on a heavier 10M-iter workload Perry is 12.3ms vs Node 72.1ms — both reasonable, no unit-scale issue. The implementation usesInstant::now()+as_secs_f64() * 1000.0which is correct. Documented in thetest_gap_console_methodsknown_failures entry.Verification
cargo fmt --all -- --checkis clean.Closes #1273.
Closes #1274.
Closes #1275.
Closes #1276.
Closes #1277.
Closes #1278.
Test plan
cargo build --release -p perry-runtime -p perry-stdlib -p perrycargo fmt --all -- --checktest_edge_buffer_from_encoding.tsbyte-for-byte vs Nodetest_issue_584_text_encoder.tsbyte-for-byte vs Nodetest_express_mount.tsbyte-for-byte vs Nodetest_issue_850_eventemitter.tsbyte-for-byte vs Nodetest_parity_buffer.tsno regressions (88→84 diff lines, mojibake fixed)test_gap_console_methods.tsonly the documented categorical gap + workload-speed timer values remainBuffer.from(arrayBuf, 1, 2)slice,console.log("for…of:", 1, 2, 3),console.table([[1,2],[3,4]]),console.table([{a:1,b:2}]),console.table([[1,2], 3, "x"])mixed