Latest Results
fix(ecmascript): apply coercion-is-pure assumption to constructor side-effect detection (#20420)
## Summary
Address review feedback from #20383 and #20395. Apply the "Coercion Methods Are Pure" assumption (`ASSUMPTIONS.md`) β `.toString()`/`.valueOf()`/`[Symbol.toPrimitive]()` are assumed side-effect-free β while still checking for spec-mandated throws that are NOT covered by that assumption.
### Changes
**`new String/Number(arg)`** β `ToString(Symbol)` / `ToNumber(Symbol)` throws TypeError. Check via `new_expr_first_arg_may_be_symbol`. BigInt is fine for both (`ToString(BigInt)` works, Number converts BigIntβNumber).
**`new Date/ArrayBuffer(arg)`** β `ToNumber(Symbol)` AND `ToNumber(BigInt)` throw TypeError. Check via `new_expr_first_arg_may_be_symbol_or_bigint`.
**Error constructors** β `new Error(Symbol())` / `Error(Symbol())` throw (`ToString(Symbol)`). Removed from `is_unconditionally_pure_constructor` and `is_pure_callable_constructor`. Added `is_error_constructor()` helper with Symbol check.
**TypedArray constructors** β use `value_type()` instead of `to_primitive()`: object args call `@@iterator` (NOT a coercion method), BigInt args cause `ToNumber` to throw. Only known-safe primitive value types (Number/String/Boolean/Null/Undefined) accepted.
**`String(x)` call** β unconditionally pure. `String()` as a function has special Symbol handling per spec (`String(Symbol())` β `"Symbol()"` without throwing). Added to `is_pure_callable_constructor`.
**`Number(x)` / `Symbol(x)` calls** β keep special handling (`ToNumeric(Symbol)` / `ToString(Symbol)` throw).
**`is_pure_collection_constructor`** β now verifies `undefined` is actually global (not shadowed). Removed invalid issue link (`XXXX`).
**Also fixes:** typo `"BitUint64Array"` β `"BigUint64Array"` in `is_known_global_constructor`.
### Cross-tool comparison
| Expression | ES Spec | esbuild | Terser | Rollup | SWC | Oxc (this PR) |
|---|---|---|---|---|---|---|
| `new String(x)` | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |
| `new Number(x)` | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |
| `new Date(x)` | impure (Symbol/BigInt) | impure | impure | pure | impure | **impure** (Symbol+BigInt) |
| `new Date(0n)` | impure (BigInt) | impure | impure | pure | impure | **impure** (BigInt check) |
| `new ArrayBuffer(x)` | impure (Symbol/BigInt) | impure | impure | pure | impure | **impure** (Symbol+BigInt) |
| `new Error(x)` | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |
| `new Uint8Array(x)` | impure (@@iterator) | impure | impure | pure | impure | **impure** (value_type) |
| `new Uint8Array({})` | impure (@@iterator) | impure | impure | pure | impure | **impure** (value_type) |
| `String(x)` call | safe (Symbol handled) | impure | impure | pure | impure | **pure** (coercion assumption) |
| `Error(x)` call | impure (Symbol) | impure | impure | pure | impure | **impure** (Symbol check) |
Oxc sits between esbuild (most conservative) and Rollup (most aggressive): uses the coercion assumption to skip ToPrimitive checks on objects, but catches spec-mandated throws (`ToString(Symbol)`, `ToNumber(Symbol)`, `ToNumber(BigInt)`, `@@iterator` on objects).
## Test plan
- [x] All 473 `oxc_minifier` tests pass (39 ignored)
- [x] All 13 `oxc_ecmascript` tests pass
- [x] Updated test expectations with new cases (`new Date(0n)`, `new ArrayBuffer(0n)`, `new Error(x)`, `Error(x)`)
- [x] Cross-tool research verified against ES Spec, esbuild, Terser, Rollup, SWC Active Branches
+14%
0%
0%
Β© 2026 CodSpeed Technology