SIGN IN SIGN UP
oven-sh / bun UNCLAIMED

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one

0 0 150 Rust

Print Infinity and negative numeric property keys as computed properties (#31328)

### Problem

Fuzzer-found invariant violation: `printed output does not reparse
(loader=ts)`.

```js
new Bun.Transpiler({ loader: "ts", minifyWhitespace: true })
  .transformSync("(class {999…(325 digits)…999() {}})")
// => (class{1/0(){}});   ← syntax error
```

A numeric property name that overflows to `Infinity` (a few hundred
digits, or just `1e999`) is printed by `print_number` as `1/0` / `1 /
0`, which is not valid syntax in property-name position. The same
happens for:

- object literal properties, methods, getters/setters: `x={1/0:1}`,
`x={get 1/0(){}}`
- class methods and fields: `class{1/0(){}}`, `class{static 1/0=1}`
- destructuring patterns: `const{1/0:x}=obj`
- negative keys produced by cross-module `const enum` inlining with
`--minify`: `{-1:1,1/0:2}`

None of these reparse, so `bun build --minify` output can be
unparseable.

### Fix

In `src/js_printer/lib.rs`, when a non-computed property key is an
`ENumber` whose printed form would not be a valid property name (sign
bit set, or `+Infinity` in the configurations where `print_number` emits
`1/0`), print it as a computed property instead:

- `print_property` (object literals, classes) — mirrors esbuild's
`printProperty` ("Automatically print numbers that would cause a syntax
error as computed properties").
- `print_binding` (destructuring object patterns) — same check;
esbuild's `printBinding` lacks it and has the same bug with `--minify`,
so this goes one step further.

Output now matches esbuild: `(class{[1/0](){}})`, `{[-1]:1,[1/0]:2}`,
`const{[1/0]:x}=obj`. Semantics are unchanged since `[1/0]`/`[-1]`
evaluate to the same property keys (`"Infinity"`, `"-1"`).

### Verification

- `test/bundler/transpiler/transpiler.test.js` — new `describe("numeric
property keys that overflow to Infinity")`: exact printed output with
and without `minifyWhitespace` for object/class/destructuring positions,
the original 325-digit fuzz input, and a runtime check that the minified
output evaluates and the key is still `"Infinity"`.
- `test/bundler/bundler_minify.test.ts` — new
`itBundled("minify/NumericPropertyKeysPrintedAsComputed")`: bundles
`1e999` keys plus an imported `const enum { Negative = -1 }` used as a
computed key, and runs the output.

Both tests fail on current canary (`(class{1/0(){}})`, bundle fails to
parse) and pass with this change; the full files pass with the debug
build (150 + 33 tests, 0 fail).


### Linked issue

Fixes #14687 — the negative-key half of this change is exactly that
report (imported `const enum` member `-1` used as an object key with
`minify: true`). Verified with the issue's repro:

```js
// before: var e={-1:"foo",1:"bar"};export{e as FooRecord};   ← syntax error
// after:  var e={[-1]:"foo",1:"bar"};export{e as FooRecord};
```
R
robobun committed
08652f2a76c030fc032383e8eed744b54f53cd31
Parent: dd0883d
Committed by GitHub <noreply@github.com> on 5/24/2026, 8:48:44 PM