fix(runtime): the empty-string key survives a packed key list - #11858
Merged
Merged
Conversation
added 2 commits
October 4, 2026 01:27
Codegen packs key names as name\0 per name, so the key "" is the single
byte \0. Every runtime reader split the packed names on NUL and dropped empty
segments, so an object literal { "": v } lost its key: the keys array came out
shorter than its count and the shape mint refused the facts, aborting at the
literal ("refusing to publish invalid object shape facts"). Across two
modules the equal contents share one static ShapeId while each module built
its own empty array, so the second module's static mint was refused -- the
natively compiled OpenCode v1.18.30 TUI died this way at startup, on json5's
JSON reviver holder { '': root }.
Read packed names through one helper that keeps empty names and drops only
the segment after the final terminator, at all seven readers.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing (5)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Symptom
Any object literal whose key is the empty string aborts:
Across modules the same bug surfaces as a different abort. This is what killed the natively compiled OpenCode v1.18.30 TUI at startup:
json5's
lib/parse.jsbuilds the standard JSON reviver holder{ '': root }, and ajv'scompile/resolve.jshas an equal one-key literal.Cause
Codegen packs a literal's key names as
name\0per name (codegen/mod.rs,expr/object_literal.rs,lower_call/new_alloc.rs), so the key""is the single byte\0. The encoding is lossless. Every reader split the packed names on NUL and then dropped empty segments (.filter(|s| !s.is_empty())), which deletes the empty key:js_build_class_keys_arraybuilt a keys array shorter than its count (0 keys forkey_count = 1), and the mint refused the facts;keysdiffered under that one id, and the second module's static mint was refused.A temporary diagnostic at the refusal printed both records' facts. Every identity field was equal except
keys, and both requests passed an empty keys array withkey_count = 1.Fix
One helper,
object::packed_key_names, keeps empty names and drops only the empty segment after the final terminator. It replaces all seven readers: four inobject/alloc.rs, the two literal seeds inobject/static_shapes.rs, and the JSON shape hint injson/parse_api.rs. It is identical to the old behaviour for every non-empty name, and it also accepts an unterminated last name. Codegen is unchanged.Tests
object::packed_key_names_testscovers"a\0b\0","\0"(one empty key), leading and trailing empty names, an unterminated last name, and empty input.crates/perry/tests/empty_string_literal_key.rscompiles a two-module program end to end and matches node 26.5.1's output byte for byte. It coversholder[""],Object.keys,in, a mixed{ "": 1, a: 2 }, a JSON reviver, a second module's own{ "": v }(the static-id path), and a literal built inside a function.Both arms were run as real commits with the runtime libraries rebuilt from each:
Perry internal error: refusing to publish invalid object shape facts.On the full OpenCode build, a stamp-pinned relink with this fix (on top of #11817, #11819, #11833 and #11839) removes the startup abort. The TUI now enters its alternate screen like bun, and then stops on a separate module-loading error, which I am investigating.
cargo fmt --all -- --checkis clean.