From 03024e6b4e3df9e55da0d4bbed2be2edadcc2746 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Fri, 19 Jul 2024 20:00:32 -0700 Subject: [PATCH] Fix truncating in BigIntStats (#12643) Co-authored-by: Jarred-Sumner --- src/bun.js/node/types.zig | 20 ++++++--- src/js/node/stream.ts | 2 +- test/bundler/expectBundled.ts | 2 +- test/cli/test/bun-test.test.ts | 2 +- .../next-pages/src/pages/index.tsx | 2 +- test/js/node/fs/fs-stats-truncate.test.ts | 43 +++++++++++++++++++ test/snippets/bundled-entry-point.js | 2 +- 7 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 test/js/node/fs/fs-stats-truncate.test.ts diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 50220361cf..e7591cfd53 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -1390,8 +1390,8 @@ pub fn StatType(comptime Big: bool) type { // Stats stores these as i32, but BigIntStats stores all of these as i64 // On windows, these two need to be u64 as the numbers are often very large. - dev: if (Environment.isWindows) u64 else Int, - ino: if (Environment.isWindows) u64 else Int, + dev: u64, + ino: u64, mode: Int, nlink: Int, uid: Int, @@ -1441,10 +1441,16 @@ pub fn StatType(comptime Big: bool) type { return struct { pub fn callback(this: *This, globalObject: *JSC.JSGlobalObject) JSC.JSValue { const value = @field(this, @tagName(field)); - if (comptime (Big and @typeInfo(@TypeOf(value)) == .Int)) { - return JSC.JSValue.fromInt64NoTruncate(globalObject, @intCast(value)); + const Type = @TypeOf(value); + if (comptime Big and @typeInfo(Type) == .Int) { + if (Type == u64) { + return JSC.JSValue.fromUInt64NoTruncate(globalObject, value); + } + + return JSC.JSValue.fromInt64NoTruncate(globalObject, value); } - return globalObject.toJS(value, .temporary); + + return JSC.JSValue.jsNumber(value); } }.callback; } @@ -1565,8 +1571,8 @@ pub fn StatType(comptime Big: bool) type { const cTime = stat_.ctime(); return .{ - .dev = if (Environment.isWindows) stat_.dev else @truncate(@as(i64, @intCast(stat_.dev))), - .ino = if (Environment.isWindows) stat_.ino else @truncate(@as(i64, @intCast(stat_.ino))), + .dev = @intCast(@max(stat_.dev, 0)), + .ino = @intCast(@max(stat_.ino, 0)), .mode = @truncate(@as(i64, @intCast(stat_.mode))), .nlink = @truncate(@as(i64, @intCast(stat_.nlink))), .uid = @truncate(@as(i64, @intCast(stat_.uid))), diff --git a/src/js/node/stream.ts b/src/js/node/stream.ts index ab4a9dffc1..48a8d44fe8 100644 --- a/src/js/node/stream.ts +++ b/src/js/node/stream.ts @@ -5720,7 +5720,7 @@ function createNativeStreamReadable(Readable) { ProcessNextTick(() => { this.push(null); }); - return view?.byteLength ?? 0 > 0 ? view : undefined; + return (view?.byteLength ?? 0 > 0) ? view : undefined; } else if ($isTypedArrayView(result)) { if (result.byteLength >= this[highWaterMark] && !this[hasResized] && !isClosed) { this[_adjustHighWaterMark](); diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index ec6f4b3e6e..195a710cda 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -1259,7 +1259,7 @@ for (const [key, blob] of build.outputs) { const outfiletext = api.readFile(path.relative(root, outfile ?? outputPaths[0])); const regex = /\/\/\s+(.+?)\nvar\s+([a-zA-Z0-9_$]+)\s+=\s+__commonJS/g; const matches = [...outfiletext.matchAll(regex)].map(match => ("/" + match[1]).replaceAll("\\", "/")); - const expectedMatches = (cjs2esm === true ? [] : cjs2esm.unhandled ?? []).map(a => a.replaceAll("\\", "/")); + const expectedMatches = (cjs2esm === true ? [] : (cjs2esm.unhandled ?? [])).map(a => a.replaceAll("\\", "/")); try { expect(matches.sort()).toEqual(expectedMatches.sort()); } catch (error) { diff --git a/test/cli/test/bun-test.test.ts b/test/cli/test/bun-test.test.ts index 784bbfbd50..908da6f667 100644 --- a/test/cli/test/bun-test.test.ts +++ b/test/cli/test/bun-test.test.ts @@ -891,7 +891,7 @@ function createTest(input?: string | (string | { filename: string; contents: str const inputs = Array.isArray(input) ? input : [input ?? ""]; for (const input of inputs) { const contents = typeof input === "string" ? input : input.contents; - const name = typeof input === "string" ? filename ?? `bun-test-${Math.random()}.test.ts` : input.filename; + const name = typeof input === "string" ? (filename ?? `bun-test-${Math.random()}.test.ts`) : input.filename; const path = join(cwd, name); try { diff --git a/test/integration/next-pages/src/pages/index.tsx b/test/integration/next-pages/src/pages/index.tsx index 109f5e5e27..b87da9145d 100644 --- a/test/integration/next-pages/src/pages/index.tsx +++ b/test/integration/next-pages/src/pages/index.tsx @@ -122,7 +122,7 @@ export async function getStaticProps() { bunVersion: process.env.NODE_ENV === "production" ? "[production needs a constant string]" - : process.versions.bun ?? "not in bun", + : (process.versions.bun ?? "not in bun"), }, }; } diff --git a/test/js/node/fs/fs-stats-truncate.test.ts b/test/js/node/fs/fs-stats-truncate.test.ts new file mode 100644 index 0000000000..a4cda48275 --- /dev/null +++ b/test/js/node/fs/fs-stats-truncate.test.ts @@ -0,0 +1,43 @@ +// BUN-2C1 +// const value = @field(this, @tagName(field)); +// if (comptime (Big and @typeInfo(@TypeOf(value)) == .Int)) { +// return JSC.JSValue.fromInt64NoTruncate(globalObject, @intCast(value)); +// } +import { Stats, statSync } from "node:fs"; +import { test, expect } from "bun:test"; + +test("fs.stats truncate", async () => { + const stats = new Stats(...Array.from({ length: 14 }, () => Number.MAX_VALUE)); + expect(stats.dev).toBeGreaterThan(0); + expect(stats.mode).toBeGreaterThan(0); + expect(stats.nlink).toBeGreaterThan(0); + expect(stats.uid).toBeGreaterThan(0); + expect(stats.gid).toBeGreaterThan(0); + expect(stats.rdev).toBeGreaterThan(0); + expect(stats.blksize).toBeGreaterThan(0); + expect(stats.ino).toBeGreaterThan(0); + expect(stats.size).toBeGreaterThan(0); + expect(stats.blocks).toBeGreaterThan(0); + expect(stats.atimeMs).toBeGreaterThan(0); + expect(stats.mtimeMs).toBeGreaterThan(0); + expect(stats.ctimeMs).toBeGreaterThan(0); + expect(stats.birthtimeMs).toBeGreaterThan(0); +}); + +test("fs.stats truncate (bigint)", async () => { + const stats = statSync(import.meta.path, { bigint: true }); + expect(stats.dev).toBeTypeOf("bigint"); + expect(stats.mode).toBeTypeOf("bigint"); + expect(stats.nlink).toBeTypeOf("bigint"); + expect(stats.uid).toBeTypeOf("bigint"); + expect(stats.gid).toBeTypeOf("bigint"); + expect(stats.rdev).toBeTypeOf("bigint"); + expect(stats.blksize).toBeTypeOf("bigint"); + expect(stats.ino).toBeTypeOf("bigint"); + expect(stats.size).toBeTypeOf("bigint"); + expect(stats.blocks).toBeTypeOf("bigint"); + expect(stats.atimeMs).toBeTypeOf("bigint"); + expect(stats.mtimeMs).toBeTypeOf("bigint"); + expect(stats.ctimeMs).toBeTypeOf("bigint"); + expect(stats.birthtimeMs).toBeTypeOf("bigint"); +}); diff --git a/test/snippets/bundled-entry-point.js b/test/snippets/bundled-entry-point.js index a996f86327..77e119729e 100644 --- a/test/snippets/bundled-entry-point.js +++ b/test/snippets/bundled-entry-point.js @@ -1,6 +1,6 @@ import "react"; -var hello = 123 ? null ?? "world" : "ok"; +var hello = 123 ? (null ?? "world") : "ok"; export function test() { return testDone(import.meta.url);