Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions examples/js_dsl/mod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ describe("primitive types", () => {
expectTypeErrorWithMessage(() => mod.doubleNumber("21"), "Argument 1 must be a number");
});

it("exactU64 accepts safe unsigned integers", () => {
expect(mod.exactU64(0)).toEqual(0);
expect(mod.exactU64(2 ** 32)).toEqual(2 ** 32);
expect(mod.exactU64(Number.MAX_SAFE_INTEGER)).toEqual(Number.MAX_SAFE_INTEGER);
});

it("exactU64 rejects values that are not exact u64 integers", () => {
for (const value of [-1, 1.5, Number.MAX_SAFE_INTEGER + 1, Infinity, -Infinity, NaN]) {
expect(() => mod.exactU64(value), `value ${value}`).toThrow("InvalidUnsignedInteger");
}
});

it("toggleBool", () => {
expect(mod.toggleBool(true)).toBe(false);
expect(mod.toggleBool(false)).toBe(true);
Expand Down
5 changes: 5 additions & 0 deletions examples/js_dsl/mod.zig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ pub fn largeUnsignedBoundary() Number {
return Number.from(@as(u64, std.math.maxInt(i64)) + 1);
}

/// Return a number validated as an exact u64 by `toU64Exact`.
pub fn exactU64(n: Number) !Number {
return Number.from(try n.toU64Exact());
}

/// Negate a boolean.
pub fn toggleBool(b: Boolean) Boolean {
return Boolean.from(!b.assertBool());
Expand Down
21 changes: 21 additions & 0 deletions src/js/number.zig
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,27 @@ pub const Number = struct {
return @intFromFloat(value);
}

/// Attempts to convert the JavaScript number to a `u64` without coercion.
///
/// Returns `error.InvalidUnsignedInteger` if the number is negative,
/// fractional, non-finite, or greater than `Number.MAX_SAFE_INTEGER`
/// (2^53 - 1).
///
/// Larger values cannot be represented exactly by a JS number;
/// use `BigInt.toU64` for the full `u64` range.
pub fn toU64Exact(self: Number) !u64 {
const max: f64 = @floatFromInt(std.math.maxInt(u53));
const value = try self.toF64();
if (!std.math.isFinite(value) or
value < 0 or
value > max or
@trunc(value) != value)
{
return error.InvalidUnsignedInteger;
}
return @intFromFloat(value);
}

/// Attempts to convert the JavaScript number to a Zig `f64`.
///
/// This conversion is generally lossless for most JS numbers, which are typically `f64`.
Expand Down
Loading