mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
b233b42045ef94de596e3890de41c755cc00a09f
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3cb1b5c7dd |
Fix CSS parser crash with large floating-point values (#21907) (#21909)
## 🐛 Problem Fixes #21907 - CSS parser was crashing with "integer part of floating point value out of bounds" when processing extremely large floating-point values like `3.40282e38px` (commonly generated by TailwindCSS `.rounded-full` class). ### Root Cause Analysis **This revealed a broader systemic issue**: The CSS parser was ported from Rust, which has different float→integer conversion semantics than Zig's `@intFromFloat`. **Zig behavior**: `@intFromFloat` panics on out-of-range values **Rust behavior**: `as` operator follows safe conversion rules: - Finite values within range: truncate toward zero - NaN: becomes 0 - Positive infinity: becomes target max value - Negative infinity: becomes target min value - Out-of-range finite values: clamp to target range The crash occurred throughout the CSS codebase wherever `@intFromFloat` was used, not just in the original failing location. ## 🔧 Comprehensive Solution ### 1. New Generic `bun.intFromFloat` Function Created a reusable function in `src/bun.zig` that implements Rust-compatible conversion semantics: ```zig pub fn intFromFloat(comptime Int: type, value: anytype) Int { // Handle NaN -> 0 if (std.math.isNan(value)) return 0; // Handle infinities -> min/max bounds if (std.math.isPositiveInf(value)) return std.math.maxInt(Int); if (std.math.isNegativeInf(value)) return std.math.minInt(Int); // Handle out-of-range values -> clamp to bounds const min_float = @as(Float, @floatFromInt(std.math.minInt(Int))); const max_float = @as(Float, @floatFromInt(std.math.maxInt(Int))); if (value > max_float) return std.math.maxInt(Int); if (value < min_float) return std.math.minInt(Int); // Safe conversion for in-range values return @as(Int, @intFromFloat(value)); } ``` ### 2. Systematic Replacement Across CSS Codebase Replaced **all 18 instances** of `@intFromFloat` in `src/css/` with `bun.intFromFloat`: | File | Conversions | Purpose | |------|-------------|---------| | `css_parser.zig` | 2 × `i32` | CSS dimension serialization | | `css_internals.zig` | 9 × `u32` | Browser target version parsing | | `values/color.zig` | 4 × `u8` | Color component conversion | | `values/color_js.zig` | 1 × `i64→u8` | Alpha channel processing | | `values/percentage.zig` | 1 × `i32` | Percentage value handling | | `properties/custom.zig` | 1 × `i32` | Color helper function | ### 3. Comprehensive Test Coverage - **New test suite**: `test/internal/int_from_float.test.ts` with inline snapshots - **Enhanced regression test**: `test/regression/issue/21907.test.ts` covering all conversion types - **Real-world testing**: Validates actual CSS processing with edge cases ## 📊 esbuild Compatibility Analysis Compared output with esbuild to ensure compatibility: **Test CSS:** ```css .test { border-radius: 3.40282e38px; } .colors { color: rgb(300, -50, 1000); } .boundaries { width: 2147483648px; } ``` **Key Differences:** 1. **Scientific notation format:** - esbuild: `3.40282e38` (no explicit + sign) - Bun: `3.40282e+38` (explicit + sign) - ✅ Both are mathematically equivalent and valid CSS 2. **Optimization strategy:** - esbuild: Preserves original literal values - Bun: Normalizes extremely large values + consolidates selectors - ✅ Bun's more aggressive optimization results in smaller output ### ❓ Question for Review **@zackradisic** - Is it acceptable for Bun to diverge from esbuild in this optimization behavior? - **Pro**: More aggressive optimization (smaller output, consistent formatting) - **Con**: Different output format than esbuild - **Impact**: Both outputs are functionally identical in browsers Should we: 1. ✅ Keep current behavior (more aggressive optimization) 2. 🔄 Match esbuild exactly (preserve literal notation) 3. 🎛️ Add flag to control this behavior ## ✅ Testing & Validation - [x] **Original crash case**: Fixed - no more panics with large floating-point values - [x] **All conversion types**: Tested i32, u32, u8, i64 conversions with edge cases - [x] **Browser compatibility**: Verified targets parsing works with extreme values - [x] **Color processing**: Confirmed RGB/RGBA values properly clamped to 0-255 range - [x] **Performance**: No regression - conversions are equally fast - [x] **Real-world**: TailwindCSS projects with `.rounded-full` work without crashes - [x] **Inline snapshots**: Capture exact expected output for future regression detection ## 🎯 Impact ### Before (Broken) ```bash $ bun build styles.css ============================================================ panic: integer part of floating point value out of bounds ``` ### After (Working) ```bash $ bun build styles.css Bundled 1 module in 93ms styles.css 121 bytes (asset) ``` - ✅ **Fixes crashes** when using TailwindCSS `.rounded-full` class on Windows - ✅ **Maintains backward compatibility** for existing projects - ✅ **Improves robustness** across all CSS float→int conversions - ✅ **Better optimization** with consistent value normalization 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> |