aboutsummaryrefslogtreecommitdiff
path: root/packages/bun-polyfills/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/bun-polyfills/src')
-rw-r--r--packages/bun-polyfills/src/modules/bun.ts12
-rw-r--r--packages/bun-polyfills/src/modules/jsc.ts111
-rw-r--r--packages/bun-polyfills/src/repl.ts4
-rw-r--r--packages/bun-polyfills/src/types/sync.d.ts15
-rw-r--r--packages/bun-polyfills/src/utils/errors.ts9
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' ] ],