diff options
Diffstat (limited to 'packages/bun-polyfills/src')
-rw-r--r-- | packages/bun-polyfills/src/modules/bun.ts | 12 | ||||
-rw-r--r-- | packages/bun-polyfills/src/modules/jsc.ts | 111 | ||||
-rw-r--r-- | packages/bun-polyfills/src/repl.ts | 4 | ||||
-rw-r--r-- | packages/bun-polyfills/src/types/sync.d.ts | 15 | ||||
-rw-r--r-- | packages/bun-polyfills/src/utils/errors.ts | 9 |
5 files changed, 148 insertions, 3 deletions
diff --git a/packages/bun-polyfills/src/modules/bun.ts b/packages/bun-polyfills/src/modules/bun.ts index ce0f6f6be..432daea95 100644 --- a/packages/bun-polyfills/src/modules/bun.ts +++ b/packages/bun-polyfills/src/modules/bun.ts @@ -30,8 +30,16 @@ import openEditor from 'open-editor'; export const main = path.resolve(process.cwd(), process.argv[1] ?? 'repl') satisfies typeof Bun.main;
-export const version = '0.7.1' satisfies typeof Bun.version; // TODO: This can probably be fetched from somewhere in the repo
-export const revision = '0'.repeat(39) + '1' satisfies typeof Bun.revision;
+//? These are automatically updated on build by tools/updateversions.ts, do not edit manually.
+export const version = '0.7.4' satisfies typeof Bun.version;
+export const revision = '7088d7e182635a58a50860302da0b1abc42c7ce7' satisfies typeof Bun.revision;
+
+export const gc = (globalThis.gc ? (() => (globalThis.gc!(), process.memoryUsage().heapUsed)) : (() => {
+ const err = new Error('[bun-polyfills] Garbage collection polyfills are only available when Node.js is ran with the --expose-gc flag.');
+ Error.captureStackTrace(err, gc);
+ throw err;
+})) satisfies typeof Bun.gc;
+
//getter(bun, 'cwd', proc.cwd); //! Can't named export a getter
export const origin = '' satisfies typeof Bun.origin;
// @ts-expect-error ---
diff --git a/packages/bun-polyfills/src/modules/jsc.ts b/packages/bun-polyfills/src/modules/jsc.ts new file mode 100644 index 000000000..45062d339 --- /dev/null +++ b/packages/bun-polyfills/src/modules/jsc.ts @@ -0,0 +1,111 @@ +import type jsc from 'bun:jsc'; +import v8 from 'node:v8'; +//import { setRandomSeed, getRandomSeed } from './mathrandom.js'; +import { NotImplementedError, getCallSites } from '../utils/errors.js'; +import { gc } from './bun.js'; + +const STUB = () => void 0; + +function jscSerialize(value: any, options?: { binaryType: 'nodebuffer'; }): Buffer; +function jscSerialize(value: any, options?: { binaryType?: 'arraybuffer'; }): SharedArrayBuffer; +function jscSerialize(value: any, options?: { binaryType?: string }): Buffer | SharedArrayBuffer { + const serialized = v8.serialize(value); + if (options?.binaryType === 'nodebuffer') return serialized; + else return new SharedArrayBuffer(serialized.byteLength); +} +// TODO: Investigate ways of making these the actual JSC serialization format (probably Bun WASM) +// TODO: whilst this works for common use-cases like Node <-> Node it still does not make it +// TODO: possible for Node <-> Bun transfers of this kind of data, which might be interesting to have. +export const serialize = jscSerialize satisfies typeof jsc.serialize; +export const deserialize = (value => { + if (value instanceof ArrayBuffer || value instanceof SharedArrayBuffer) return v8.deserialize(Buffer.from(value)); + else return v8.deserialize(value); +}) satisfies typeof jsc.deserialize; + +export const setTimeZone = ((timeZone: string) => { + const resolvedTZ = Intl.DateTimeFormat(undefined, { timeZone }).resolvedOptions().timeZone; + return process.env.TZ = resolvedTZ; +}) satisfies typeof jsc.setTimeZone; + +export const callerSourceOrigin = (() => { + const callsites: NodeJS.CallSite[] = getCallSites(2); + // This may be inaccurate with async code. Needs more testing. + let lastSeenURL = ''; + for (const callsite of callsites) { + const sourceURL = callsite.getScriptNameOrSourceURL(); + if (sourceURL.startsWith('file://')) lastSeenURL = sourceURL; + } + return lastSeenURL; +}) satisfies typeof jsc.callerSourceOrigin; + +// TODO: Like with jsc.serialize/deserialize, these may be possible with Bun WASM. +export const jscDescribe = (() => { throw new NotImplementedError('jsc.jscDescribe', STUB); }) satisfies typeof jsc.jscDescribe; +export const jscDescribeArray = (() => { throw new NotImplementedError('jsc.jscDescribeArray', STUB); }) satisfies typeof jsc.jscDescribeArray; +// These are no longer documented but still exist. +export const describe = jscDescribe; +export const describeArray = jscDescribeArray; + +// Node.js only provides a singular non-configurable global GC function, so we have to make do with that. +export const edenGC = gc satisfies typeof jsc.edenGC; +export const fullGC = gc satisfies typeof jsc.fullGC; +export const gcAndSweep = gc satisfies typeof jsc.gcAndSweep; + +export const drainMicrotasks = STUB satisfies typeof jsc.drainMicrotasks; // no-op +export const releaseWeakRefs = STUB satisfies typeof jsc.releaseWeakRefs; // no-op +export const startSamplingProfiler = STUB satisfies typeof jsc.startSamplingProfiler; // no-op +//! likely broken but needs more testing +export const startRemoteDebugger = STUB satisfies typeof jsc.startRemoteDebugger; // no-op + +//! this is a really poor polyfill but it's better than nothing +export const getProtectedObjects = (() => { return [globalThis]; }) satisfies typeof jsc.getProtectedObjects; + +export const getRandomSeed = 0; // TODO +export const setRandomSeed = 0; // TODO + +export const heapSize = (() => { return v8.getHeapStatistics().used_heap_size; }) satisfies typeof jsc.heapSize; +export const heapStats = (() => { + const stats = v8.getHeapStatistics(); + return { + heapSize: stats.used_heap_size, + heapCapacity: stats.total_available_size, + extraMemorySize: stats.external_memory ?? 0, + objectCount: 1, // TODO: how to get this in node? + protectedObjectCount: getProtectedObjects().length, + globalObjectCount: 2, // TODO: this one is probably fine hardcoded but is there a way to get this in node? + protectedGlobalObjectCount: 1, // TODO: ^ + objectTypeCounts: {}, //! can't really throw an error here, so just return an empty object (TODO: how to get this in node?) + protectedObjectTypeCounts: {} //! can't really throw an error here, so just return an empty object (TODO: how to get this in node?) + }; +}) satisfies typeof jsc.heapStats; + +//! doubtful anyone relies on the return of this for anything besides debugging +export const isRope = (() => false) satisfies typeof jsc.isRope; + +export const memoryUsage = (() => { + const stats = v8.getHeapStatistics(); + const resUse = process.resourceUsage(); + return { + current: stats.malloced_memory, + peak: stats.peak_malloced_memory, + currentCommit: stats.malloced_memory, + peakCommit: stats.malloced_memory, + pageFaults: resUse.minorPageFault + resUse.majorPageFault + }; +}) satisfies typeof jsc.memoryUsage; + +//! these are likely broken, seemingly always returning undefined which does not match the documented return types +export const noFTL = (() => { return void 0 as unknown as Function; }) satisfies typeof jsc.noFTL; +export const noOSRExitFuzzing = (() => { return void 0 as unknown as Function; }) satisfies typeof jsc.noOSRExitFuzzing; +//! likely broken, seems to always returns zero +export const totalCompileTime = (() => 0) satisfies typeof jsc.totalCompileTime; +//! likely broken, seem to always returns 0 if any arguments are passed, undefined otherwise +export const numberOfDFGCompiles = ((...args) => args.length ? 0 : void 0 as unknown as number) satisfies typeof jsc.numberOfDFGCompiles; +export const reoptimizationRetryCount = ((...args) => args.length ? 0 : void 0 as unknown as number) satisfies typeof jsc.reoptimizationRetryCount; + +//! The following are very likely impossible to ever polyfill. +export const profile = (() => { + throw new NotImplementedError('jsc.profile is not polyfillable', STUB, true); +}) satisfies typeof jsc.profile; +export const optimizeNextInvocation = (() => { + throw new NotImplementedError('jsc.optimizeNextInvocation is not polyfillable', STUB, true); +}) satisfies typeof jsc.optimizeNextInvocation; diff --git a/packages/bun-polyfills/src/repl.ts b/packages/bun-polyfills/src/repl.ts index 5cf673701..030e479b0 100644 --- a/packages/bun-polyfills/src/repl.ts +++ b/packages/bun-polyfills/src/repl.ts @@ -1,4 +1,5 @@ import bun from './index.js'; +import * as jsc from './modules/jsc.js'; // This file serves two purposes: // 1. It is the entry point for using the Bun global in the REPL. (--import this file) @@ -12,7 +13,6 @@ globalThis.Bun = bun as typeof bun & { deepMatch: typeof import('bun').deepMatch; build: typeof import('bun').build; mmap: typeof import('bun').mmap; - gc: typeof import('bun').gc; connect: typeof import('bun').connect; listen: typeof import('bun').listen; Transpiler: typeof import('bun').Transpiler; @@ -27,3 +27,5 @@ globalThis.Bun = bun as typeof bun & { stderr: typeof import('bun').stderr; stdin: typeof import('bun').stdin; }; + +Reflect.set(globalThis, 'jsc', jsc); diff --git a/packages/bun-polyfills/src/types/sync.d.ts b/packages/bun-polyfills/src/types/sync.d.ts index 87e6794e0..11ed63a17 100644 --- a/packages/bun-polyfills/src/types/sync.d.ts +++ b/packages/bun-polyfills/src/types/sync.d.ts @@ -12,4 +12,19 @@ declare module 'stream/web' { declare global { var performance: typeof import('perf_hooks').performance; + + // TODO: These should be contributed to @types/node upstream + namespace NodeJS { + interface CallSite { + getScriptNameOrSourceURL(): string; + getEnclosingColumnNumber(): number; + getEnclosingLineNumber(): number; + getPosition(): number; + getPromiseIndex(): number; + getScriptHash(): string; + isAsync(): boolean; + isPromiseAll(): boolean; + toString(): string; + } + } } diff --git a/packages/bun-polyfills/src/utils/errors.ts b/packages/bun-polyfills/src/utils/errors.ts index cc82efdf0..35b967436 100644 --- a/packages/bun-polyfills/src/utils/errors.ts +++ b/packages/bun-polyfills/src/utils/errors.ts @@ -1,6 +1,15 @@ type PosixErrNo = MapKeysType<ReturnType<typeof getPosixSystemErrorMap>>;
type Win32ErrNo = MapKeysType<ReturnType<typeof getWin32SystemErrorMap>>;
+export function getCallSites(sliceOff = 1) {
+ const originalPST = Error.prepareStackTrace;
+ Error.prepareStackTrace = (error, stack) => stack;
+ const { stack } = new Error();
+ if (stack?.constructor.name !== 'Array') throw new Error('Failed to acquire structured JS stack trace');
+ Error.prepareStackTrace = originalPST;
+ return (stack as unknown as NodeJS.CallSite[]).slice(sliceOff);
+}
+
export function getPosixSystemErrorMap() {
return new Map([
[ -7, [ 'E2BIG', 'argument list too long' ] ],
|