Skip to content

fix(runtime+codegen): #1002 — native util.format / util.formatWithOptions#1011

Merged
proggeramlug merged 1 commit into
mainfrom
fix/1002-util-format-native
May 18, 2026
Merged

fix(runtime+codegen): #1002 — native util.format / util.formatWithOptions#1011
proggeramlug merged 1 commit into
mainfrom
fix/1002-util-format-native

Conversation

@proggeramlug
Copy link
Copy Markdown
Contributor

Closes #1002.

Summary

#948 added util.format / util.formatWithOptions to the API manifest
(lifting the #463 gate) but never wired a native runtime. Both calls
fell through the receiver-less native-method-call early-out in
lower_call/native.rs and returned TAG_UNDEFINED:

console.log(util.format("Hello %s", "world"));         // → "undefined"
console.log(util.formatWithOptions({}, "x=%s y=%d", "k", 7));  // → "undefined"

test_util_format_with_options has been the lone NEW FAILURES entry on
the parity gate since #948 landed — admin-bypassed on #944, #947, #958,
#997, #1000, #1003, #1004, #1009.

Fix

Three small pieces:

  1. crates/perry-runtime/src/builtins.rs — new
    js_util_format(arr_ptr) -> f64. Walks an array whose element 0 is
    the format string and 1.. are substitution values. Handles
    %s / %d / %i / %f / %j / %o / %O / %% and appends
    any trailing args space-separated. Falls back to a space-joined
    format_jsvalue when the first arg isn't a string (same shape as
    Node's util.format).

  2. crates/perry-codegen/src/runtime_decls.rs — declare the
    extern.

  3. crates/perry-codegen/src/lower_call/native.rs — ahead of the
    receiver-less fall-through, recognize util.format /
    util.formatWithOptions and bundle the call args into a heap array
    (mirrors js_console_log_spread), then dispatch.
    formatWithOptions drops args[0] (the util.inspect options
    bag) and delegates to the same runtime — full options-passthrough
    (real %o/%O styling with colors, depth, breakLength, …)
    is a follow-up.

Test plan

cargo build --release -p perry-runtime -p perry-stdlib -p perry
./target/release/perry test-files/test_util_format_with_options.ts -o /tmp/p && /tmp/p
node --experimental-strip-types test-files/test_util_format_with_options.ts
  • Before fix: undefined\nundefined.
  • After fix: Hello world\nx=k y=7 — exact parity with node --experimental-strip-types.
  • scripts/run_native_no_fallback_tests.sh — 35 passed, 0 failed (no regression on the broader smoke).

…ions

The manifest entries for `util.format` and `util.formatWithOptions` lifted
the #463 gate but no native runtime was wired up. Both calls fell through
the receiver-less native-method-call early-out in `lower_call/native.rs`
and returned `TAG_UNDEFINED` — `console.log(util.format("hi %s", "x"))`
printed `undefined`, and `test_util_format_with_options` had been the
parity gate's lone NEW failure since #948 added the manifest entry.

Three small pieces:

- runtime/builtins.rs: new `js_util_format(arr_ptr)` walks a heap array
  whose element 0 is the format string and 1.. are substitution values.
  Handles `%s` / `%d` / `%i` / `%f` / `%j` / `%o` / `%O` / `%%` and
  appends any trailing args space-separated — same shape as Node's
  `util.format`.
- codegen/runtime_decls.rs: declare the extern.
- codegen/lower_call/native.rs: ahead of the receiver-less fall-through,
  recognize `util.format` / `util.formatWithOptions`, bundle the args
  into a heap array (mirrors `js_console_log_spread`), and dispatch.
  `formatWithOptions` drops `args[0]` (the inspect-options bag) and
  delegates to the same runtime — full options-passthrough (real
  `%o`/`%O` styling with colors/depth/breakLength) is a follow-up.

Validation:

- `node test-files/test_util_format_with_options.ts` and the perry
  binary now produce the same output (`Hello world` + `x=k y=7`).
- `scripts/run_native_no_fallback_tests.sh` — 35 passed, 0 failed (no
  regression on the broader smoke).
@proggeramlug proggeramlug merged commit aaf7659 into main May 18, 2026
5 of 9 checks passed
@proggeramlug proggeramlug deleted the fix/1002-util-format-native branch May 18, 2026 09:20
proggeramlug added a commit that referenced this pull request May 18, 2026
Both PRs were admin-merged with lint red. Land the trivial fmt cleanup
so subsequent PRs don't inherit the lint failure on top of their own
diffs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test_util_format_with_options: formatWithOptions returns undefined instead of delegating to util.format

1 participant