aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/bun-test.yml43
-rw-r--r--Dockerfile10
-rwxr-xr-xpackages/bun-release/bun.lockbbin37451 -> 36604 bytes
-rwxr-xr-xpackages/bun-test/bun.lockbbin1575 -> 2898 bytes
-rw-r--r--packages/bun-test/package.json4
-rw-r--r--packages/bun-test/src/runner.ts27
-rw-r--r--packages/bun-types/bun.d.ts38
-rw-r--r--packages/bun-types/globals.d.ts204
-rw-r--r--src/bun.js/api/bun.zig34
-rw-r--r--src/bun.js/bindings/JSBuffer.cpp158
-rw-r--r--src/bun.js/bindings/JSBufferEncodingType.cpp79
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp57
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h3
-rw-r--r--src/bun.js/crypto.exports.js32
-rw-r--r--src/bun.js/node/node_os.zig140
-rw-r--r--src/bun.js/perf_hooks.exports.js1
-rw-r--r--src/bun.js/util.exports.js8
-rw-r--r--src/bun.js/webcore/encoding.zig49
-rw-r--r--src/node-fallbacks/package-lock.json43
-rw-r--r--src/node-fallbacks/package.json2
-rw-r--r--src/string_immutable.zig64
-rw-r--r--test/bun.js/node-crypto.test.js6
-rw-r--r--test/bun.js/node-dns.test.js2
-rw-r--r--test/bun.js/setTimeout.test.js46
-rw-r--r--test/bun.js/text-decoder.test.js132
25 files changed, 796 insertions, 386 deletions
diff --git a/.github/workflows/bun-test.yml b/.github/workflows/bun-test.yml
new file mode 100644
index 000000000..02a9a2383
--- /dev/null
+++ b/.github/workflows/bun-test.yml
@@ -0,0 +1,43 @@
+name: bun-test
+on:
+ push:
+ branches:
+ - main
+ - "test/*"
+ paths:
+ - "src/**/*"
+ - "test/**/*"
+ pull_request:
+ branches:
+ - main
+ - "test/*"
+ paths:
+ - "src/**/*"
+ - "test/**/*"
+ workflow_dispatch:
+ inputs:
+ release:
+ type: string
+ default: canary
+jobs:
+ bun:
+ name: Bun
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: packages/bun-test
+ steps:
+ - id: checkout
+ name: Checkout
+ uses: actions/checkout@v3
+ - id: setup-bun
+ name: Setup Bun
+ uses: oven-sh/setup-bun@v0.1.8
+ with:
+ bun-version: ${{ github.event.inputs.release || 'canary' }}
+ - id: setup-dependencies
+ name: Setup Dependencies
+ run: bun install
+ - id: test
+ name: Test
+ run: bun run test
diff --git a/Dockerfile b/Dockerfile
index 479fb44be..9e2c66e9d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -122,8 +122,8 @@ ARG BUN_RELEASE_DIR
ARG BUN_DEPS_OUT_DIR
ARG BUN_DIR
ARG CPU_TARGET
-
ENV CPU_TARGET=${CPU_TARGET}
+
ENV CCACHE_DIR=/ccache
ENV JSC_BASE_DIR=${WEBKIT_DIR}
ENV LIB_ICU_PATH=${WEBKIT_DIR}/lib
@@ -149,6 +149,9 @@ ARG BUN_RELEASE_DIR
ARG BUN_DEPS_OUT_DIR
ARG BUN_DIR
+ARG CPU_TARGET
+ENV CPU_TARGET=${CPU_TARGET}
+
COPY Makefile ${BUN_DIR}/Makefile
COPY src/deps/lol-html ${BUN_DIR}/src/deps/lol-html
@@ -303,8 +306,6 @@ ARG BUN_RELEASE_DIR
ARG BUN_DEPS_OUT_DIR
ARG BUN_DIR
ARG CPU_TARGET
-
-
ENV CPU_TARGET=${CPU_TARGET}
COPY Makefile ${BUN_DIR}/Makefile
@@ -484,6 +485,9 @@ ARG BUN_RELEASE_DIR
ARG BUN_DEPS_OUT_DIR
ARG BUN_DIR
+ARG CPU_TARGET
+ENV CPU_TARGET=${CPU_TARGET}
+
ENV CCACHE_DIR=/ccache
COPY Makefile ${BUN_DIR}/Makefile
diff --git a/packages/bun-release/bun.lockb b/packages/bun-release/bun.lockb
index 769154b6c..9ae1c3b95 100755
--- a/packages/bun-release/bun.lockb
+++ b/packages/bun-release/bun.lockb
Binary files differ
diff --git a/packages/bun-test/bun.lockb b/packages/bun-test/bun.lockb
index ac8b84c5f..b5a1b7c3e 100755
--- a/packages/bun-test/bun.lockb
+++ b/packages/bun-test/bun.lockb
Binary files differ
diff --git a/packages/bun-test/package.json b/packages/bun-test/package.json
index 531b93ec8..6504f57b4 100644
--- a/packages/bun-test/package.json
+++ b/packages/bun-test/package.json
@@ -1,6 +1,8 @@
{
"private": true,
- "dependencies": {},
+ "dependencies": {
+ "@actions/core": "^1.10.0"
+ },
"devDependencies": {
"bun-types": "canary",
"prettier": "^2.8.2"
diff --git a/packages/bun-test/src/runner.ts b/packages/bun-test/src/runner.ts
index 37ae1ed05..256d0c87f 100644
--- a/packages/bun-test/src/runner.ts
+++ b/packages/bun-test/src/runner.ts
@@ -1,6 +1,7 @@
import { spawn } from "bun";
import { readdirSync } from "node:fs";
import { resolve } from "node:path";
+import * as action from "@actions/core";
const cwd = resolve("../..");
const isAction = !!process.env["GITHUB_ACTION"];
@@ -38,7 +39,7 @@ async function runTest(path: string): Promise<void> {
const prefix = exitCode === 0
? "PASS"
: `FAIL (exit code ${exitCode})`;
- console.log(`::group::${prefix} - ${name}`);
+ action.startGroup(`${prefix} - ${name}`);
}
for (const stdout of [runner.stdout, runner.stderr]) {
if (!stdout) {
@@ -48,7 +49,10 @@ async function runTest(path: string): Promise<void> {
while (true) {
const { value, done } = await reader.read();
if (value) {
- write(value);
+ console.write(value);
+ if (isAction) {
+ findErrors(value);
+ }
}
if (done) {
break;
@@ -56,19 +60,21 @@ async function runTest(path: string): Promise<void> {
}
}
if (isAction) {
- console.log("::endgroup::");
+ action.endGroup();
}
}
-function write(data: Uint8Array): void {
- console.write(data);
- if (!isAction) {
- return;
- }
+let failed = false;
+
+function findErrors(data: Uint8Array): void {
const text = new TextDecoder().decode(data);
for (const [message, _, path, line, col] of text.matchAll(errorPattern)) {
- const name = path.replace(cwd, "").slice(1);
- console.log(`::error file=${name},line=${line},col=${col},title=${message}::`);
+ failed = true;
+ action.error(message, {
+ file: path.replace(cwd, "").slice(1),
+ startLine: parseInt(line),
+ startColumn: parseInt(col),
+ });
}
}
@@ -77,3 +83,4 @@ for (const path of findTests(resolve(cwd, "test/bun.js"))) {
tests.push(runTest(path).catch(console.error));
}
await Promise.allSettled(tests);
+process.exit(failed ? 1 : 0);
diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts
index 0b0ceb7eb..ba626c00d 100644
--- a/packages/bun-types/bun.d.ts
+++ b/packages/bun-types/bun.d.ts
@@ -767,7 +767,7 @@ declare module "bun" {
* const query = UserQuery;
* ```
*/
- macros?: MacroMap;
+ macro?: MacroMap;
autoImportJSX?: boolean;
allowBunRuntime?: boolean;
@@ -1785,7 +1785,7 @@ declare module "bun" {
*
*/
// tslint:disable-next-line:unified-signatures
- export function file(path: string, options?: BlobPropertyBag): FileBlob;
+ export function file(path: string | URL, options?: BlobPropertyBag): FileBlob;
/**
* `Blob` that leverages the fastest system calls available to operate on files.
@@ -2152,6 +2152,40 @@ declare module "bun" {
}
/**
+ * Resolve a `Promise` after milliseconds. This is like
+ * {@link setTimeout} except it returns a `Promise`.
+ *
+ * @param ms milliseconds to delay resolving the promise. This is a minimum
+ * number. It may take longer. If a {@link Date} is passed, it will sleep until the
+ * {@link Date} is reached.
+ *
+ * @example
+ * ## Sleep for 1 second
+ * ```ts
+ * import { sleep } from "bun";
+ *
+ * await sleep(1000);
+ * ```
+ * ## Sleep for 10 milliseconds
+ * ```ts
+ * await Bun.sleep(10);
+ * ```
+ * ## Sleep until `Date`
+ *
+ * ```ts
+ * const target = new Date();
+ * target.setSeconds(target.getSeconds() + 1);
+ * await Bun.sleep(target);
+ * ```
+ * Internally, `Bun.sleep` is the equivalent of
+ * ```ts
+ * await new Promise((resolve) => setTimeout(resolve, ms));
+ * ```
+ * As always, you can use `Bun.sleep` or the imported `sleep` function interchangeably.
+ */
+ export function sleep(ms: number | Date): Promise<void>;
+
+ /**
* Sleep the thread for a given number of milliseconds
*
* This is a blocking function.
diff --git a/packages/bun-types/globals.d.ts b/packages/bun-types/globals.d.ts
index 1c91d6b2f..cef3f57ec 100644
--- a/packages/bun-types/globals.d.ts
+++ b/packages/bun-types/globals.d.ts
@@ -14,17 +14,7 @@ type Platform =
| "win32"
| "cygwin"
| "netbsd";
-type Architecture =
- | "arm"
- | "arm64"
- | "ia32"
- | "mips"
- | "mipsel"
- | "ppc"
- | "ppc64"
- | "s390"
- | "s390x"
- | "x64";
+type Architecture = "arm" | "arm64" | "ia32" | "mips" | "mipsel" | "ppc" | "ppc64" | "s390" | "s390x" | "x64";
type Signals =
| "SIGABRT"
| "SIGALRM"
@@ -69,7 +59,7 @@ interface ArrayConstructor {
asyncItems: AsyncIterable<T> | Iterable<T> | ArrayLike<T>,
mapfn?: (value: any, index: number) => any,
thisArg?: any,
- ): Array<T>;
+ ): Promise<Array<T>>;
}
interface Console {
@@ -446,10 +436,7 @@ interface Headers {
entries(): IterableIterator<[string, string]>;
keys(): IterableIterator<string>;
values(): IterableIterator<string>;
- forEach(
- callbackfn: (value: string, key: string, parent: Headers) => void,
- thisArg?: any,
- ): void;
+ forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;
/**
* Convert {@link Headers} to a plain JavaScript object.
@@ -493,13 +480,7 @@ declare var Headers: {
};
type HeadersInit = Array<[string, string]> | Record<string, string> | Headers;
-type ResponseType =
- | "basic"
- | "cors"
- | "default"
- | "error"
- | "opaque"
- | "opaqueredirect";
+type ResponseType = "basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect";
declare class Blob implements BlobInterface {
/**
@@ -575,10 +556,7 @@ interface ResponseInit {
* ```
*/
declare class Response implements BlobInterface {
- constructor(
- body?: ReadableStream | BlobPart | BlobPart[] | null,
- options?: ResponseInit,
- );
+ constructor(body?: ReadableStream | BlobPart | BlobPart[] | null, options?: ResponseInit);
/**
* Create a new {@link Response} with a JSON body
@@ -711,13 +689,7 @@ declare class Response implements BlobInterface {
clone(): Response;
}
-type RequestCache =
- | "default"
- | "force-cache"
- | "no-cache"
- | "no-store"
- | "only-if-cached"
- | "reload";
+type RequestCache = "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload";
type RequestCredentials = "include" | "omit" | "same-origin";
type RequestDestination =
| ""
@@ -757,9 +729,7 @@ type RequestInfo = Request | string;
type BodyInit = ReadableStream | XMLHttpRequestBodyInit;
type XMLHttpRequestBodyInit = Blob | BufferSource | string;
type ReadableStreamController<T> = ReadableStreamDefaultController<T>;
-type ReadableStreamDefaultReadResult<T> =
- | ReadableStreamDefaultReadValueResult<T>
- | ReadableStreamDefaultReadDoneResult;
+type ReadableStreamDefaultReadResult<T> = ReadableStreamDefaultReadValueResult<T> | ReadableStreamDefaultReadDoneResult;
type ReadableStreamReader<T> = ReadableStreamDefaultReader<T>;
interface RequestInit {
@@ -1091,10 +1061,7 @@ declare class TextDecoder {
*/
readonly ignoreBOM: boolean;
- constructor(
- encoding?: Encoding,
- options?: { fatal?: boolean; ignoreBOM?: boolean },
- );
+ constructor(encoding?: Encoding, options?: { fatal?: boolean; ignoreBOM?: boolean });
/**
* Decodes the `input` and returns a string. If `options.stream` is `true`, any
@@ -1252,10 +1219,9 @@ declare function clearTimeout(id?: number): void;
*
*
*/
-declare function fetch(
- url: string | URL,
- init?: FetchRequestInit,
-): Promise<Response>;
+
+declare function fetch(url: string | URL, init?: FetchRequestInit): Promise<Response>;
+
/**
* Send a HTTP(s) request
@@ -1280,30 +1246,19 @@ declare function reportError(error: any): void;
* Run a function immediately after main event loop is vacant
* @param handler function to call
*/
-declare function setImmediate(
- handler: TimerHandler,
- ...arguments: any[]
-): number;
+declare function setImmediate(handler: TimerHandler, ...arguments: any[]): number;
/**
* Run a function every `interval` milliseconds
* @param handler function to call
* @param interval milliseconds to wait between calls
*/
-declare function setInterval(
- handler: TimerHandler,
- interval?: number,
- ...arguments: any[]
-): number;
+declare function setInterval(handler: TimerHandler, interval?: number, ...arguments: any[]): number;
/**
* Run a function after `timeout` (milliseconds)
* @param handler function to call
* @param timeout milliseconds to wait between calls
*/
-declare function setTimeout(
- handler: TimerHandler,
- timeout?: number,
- ...arguments: any[]
-): number;
+declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
declare function addEventListener<K extends keyof EventMap>(
type: K,
listener: (this: object, ev: EventMap[K]) => any,
@@ -1751,19 +1706,14 @@ interface URLSearchParams {
keys(): IterableIterator<string>;
/** Returns an iterator allowing to go through all values of the key/value pairs of this search parameter. */
values(): IterableIterator<string>;
- forEach(
- callbackfn: (value: string, key: string, parent: URLSearchParams) => void,
- thisArg?: any,
- ): void;
+ forEach(callbackfn: (value: string, key: string, parent: URLSearchParams) => void, thisArg?: any): void;
/** Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */
toString(): string;
}
declare var URLSearchParams: {
prototype: URLSearchParams;
- new (
- init?: string[][] | Record<string, string> | string | URLSearchParams,
- ): URLSearchParams;
+ new (init?: string[][] | Record<string, string> | string | URLSearchParams): URLSearchParams;
toString(): string;
};
@@ -1964,19 +1914,10 @@ interface ReadableStream<R = any> {
readonly locked: boolean;
cancel(reason?: any): Promise<void>;
getReader(): ReadableStreamDefaultReader<R>;
- pipeThrough<T>(
- transform: ReadableWritablePair<T, R>,
- options?: StreamPipeOptions,
- ): ReadableStream<T>;
- pipeTo(
- destination: WritableStream<R>,
- options?: StreamPipeOptions,
- ): Promise<void>;
+ pipeThrough<T>(transform: ReadableWritablePair<T, R>, options?: StreamPipeOptions): ReadableStream<T>;
+ pipeTo(destination: WritableStream<R>, options?: StreamPipeOptions): Promise<void>;
tee(): [ReadableStream<R>, ReadableStream<R>];
- forEach(
- callbackfn: (value: any, key: number, parent: ReadableStream<R>) => void,
- thisArg?: any,
- ): void;
+ forEach(callbackfn: (value: any, key: number, parent: ReadableStream<R>) => void, thisArg?: any): void;
[Symbol.asyncIterator](): AsyncIterableIterator<R>;
values(options?: { preventCancel: boolean }): AsyncIterableIterator<R>;
}
@@ -2034,8 +1975,7 @@ declare var ReadableStreamDefaultController: {
new (): ReadableStreamDefaultController;
};
-interface ReadableStreamDefaultReader<R = any>
- extends ReadableStreamGenericReader {
+interface ReadableStreamDefaultReader<R = any> extends ReadableStreamGenericReader {
read(): Promise<ReadableStreamDefaultReadResult<R>>;
releaseLock(): void;
}
@@ -2080,10 +2020,7 @@ interface WritableStream<W = any> {
declare var WritableStream: {
prototype: WritableStream;
- new <W = any>(
- underlyingSink?: UnderlyingSink<W>,
- strategy?: QueuingStrategy<W>,
- ): WritableStream<W>;
+ new <W = any>(underlyingSink?: UnderlyingSink<W>, strategy?: QueuingStrategy<W>): WritableStream<W>;
};
/** This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate. */
@@ -2123,10 +2060,7 @@ interface TransformerStartCallback<O> {
}
interface TransformerTransformCallback<I, O> {
- (
- chunk: I,
- controller: TransformStreamDefaultController<O>,
- ): void | PromiseLike<void>;
+ (chunk: I, controller: TransformStreamDefaultController<O>): void | PromiseLike<void>;
}
interface UnderlyingSinkAbortCallback {
@@ -2142,10 +2076,7 @@ interface UnderlyingSinkStartCallback {
}
interface UnderlyingSinkWriteCallback<W> {
- (
- chunk: W,
- controller: WritableStreamDefaultController,
- ): void | PromiseLike<void>;
+ (chunk: W, controller: WritableStreamDefaultController): void | PromiseLike<void>;
}
interface UnderlyingSourceCancelCallback {
@@ -2170,9 +2101,7 @@ interface UnderlyingSource<R = any> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface DirectUnderlyingSource<R = any> {
cancel?: UnderlyingSourceCancelCallback;
- pull: (
- controller: ReadableStreamDirectController,
- ) => void | PromiseLike<void>;
+ pull: (controller: ReadableStreamDirectController) => void | PromiseLike<void>;
type: "direct";
}
@@ -2289,15 +2218,7 @@ declare function prompt(message?: string, _default?: string): string | null;
type KeyFormat = "jwk" | "pkcs8" | "raw" | "spki";
type KeyType = "private" | "public" | "secret";
-type KeyUsage =
- | "decrypt"
- | "deriveBits"
- | "deriveKey"
- | "encrypt"
- | "sign"
- | "unwrapKey"
- | "verify"
- | "wrapKey";
+type KeyUsage = "decrypt" | "deriveBits" | "deriveKey" | "encrypt" | "sign" | "unwrapKey" | "verify" | "wrapKey";
type HashAlgorithmIdentifier = AlgorithmIdentifier;
type NamedCurve = string;
@@ -2450,59 +2371,30 @@ type AlgorithmIdentifier = Algorithm | string;
*/
interface SubtleCrypto {
decrypt(
- algorithm:
- | AlgorithmIdentifier
- | RsaOaepParams
- | AesCtrParams
- | AesCbcParams
- | AesGcmParams,
+ algorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams,
key: CryptoKey,
data: BufferSource,
): Promise<ArrayBuffer>;
deriveBits(
- algorithm:
- | AlgorithmIdentifier
- | EcdhKeyDeriveParams
- | HkdfParams
- | Pbkdf2Params,
+ algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params,
baseKey: CryptoKey,
length: number,
): Promise<ArrayBuffer>;
deriveKey(
- algorithm:
- | AlgorithmIdentifier
- | EcdhKeyDeriveParams
- | HkdfParams
- | Pbkdf2Params,
+ algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params,
baseKey: CryptoKey,
- derivedKeyType:
- | AlgorithmIdentifier
- | AesDerivedKeyParams
- | HmacImportParams
- | HkdfParams
- | Pbkdf2Params,
+ derivedKeyType: AlgorithmIdentifier | AesDerivedKeyParams | HmacImportParams | HkdfParams | Pbkdf2Params,
extractable: boolean,
keyUsages: KeyUsage[],
): Promise<CryptoKey>;
- digest(
- algorithm: AlgorithmIdentifier,
- data: BufferSource,
- ): Promise<ArrayBuffer>;
+ digest(algorithm: AlgorithmIdentifier, data: BufferSource): Promise<ArrayBuffer>;
encrypt(
- algorithm:
- | AlgorithmIdentifier
- | RsaOaepParams
- | AesCtrParams
- | AesCbcParams
- | AesGcmParams,
+ algorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams,
key: CryptoKey,
data: BufferSource,
): Promise<ArrayBuffer>;
exportKey(format: "jwk", key: CryptoKey): Promise<JsonWebKey>;
- exportKey(
- format: Exclude<KeyFormat, "jwk">,
- key: CryptoKey,
- ): Promise<ArrayBuffer>;
+ exportKey(format: Exclude<KeyFormat, "jwk">, key: CryptoKey): Promise<ArrayBuffer>;
generateKey(
algorithm: RsaHashedKeyGenParams | EcKeyGenParams,
extractable: boolean,
@@ -2521,24 +2413,14 @@ interface SubtleCrypto {
importKey(
format: "jwk",
keyData: JsonWebKey,
- algorithm:
- | AlgorithmIdentifier
- | RsaHashedImportParams
- | EcKeyImportParams
- | HmacImportParams
- | AesKeyAlgorithm,
+ algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm,
extractable: boolean,
keyUsages: ReadonlyArray<KeyUsage>,
): Promise<CryptoKey>;
importKey(
format: Exclude<KeyFormat, "jwk">,
keyData: BufferSource,
- algorithm:
- | AlgorithmIdentifier
- | RsaHashedImportParams
- | EcKeyImportParams
- | HmacImportParams
- | AesKeyAlgorithm,
+ algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm,
extractable: boolean,
keyUsages: KeyUsage[],
): Promise<CryptoKey>;
@@ -2551,12 +2433,7 @@ interface SubtleCrypto {
format: KeyFormat,
wrappedKey: BufferSource,
unwrappingKey: CryptoKey,
- unwrapAlgorithm:
- | AlgorithmIdentifier
- | RsaOaepParams
- | AesCtrParams
- | AesCbcParams
- | AesGcmParams,
+ unwrapAlgorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams,
unwrappedKeyAlgorithm:
| AlgorithmIdentifier
| RsaHashedImportParams
@@ -2576,12 +2453,7 @@ interface SubtleCrypto {
format: KeyFormat,
key: CryptoKey,
wrappingKey: CryptoKey,
- wrapAlgorithm:
- | AlgorithmIdentifier
- | RsaOaepParams
- | AesCtrParams
- | AesCbcParams
- | AesGcmParams,
+ wrapAlgorithm: AlgorithmIdentifier | RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams,
): Promise<ArrayBuffer>;
}
@@ -2665,9 +2537,7 @@ interface ErrorConstructor {
*
* @see https://v8.dev/docs/stack-trace-api#customizing-stack-traces
*/
- prepareStackTrace?:
- | ((err: Error, stackTraces: CallSite[]) => any)
- | undefined;
+ prepareStackTrace?: ((err: Error, stackTraces: CallSite[]) => any) | undefined;
stackTraceLimit: number;
}
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 0b47bea27..da42e5feb 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2873,19 +2873,25 @@ pub const Timer = struct {
const callback = this.callback.get() orelse @panic("Expected CallbackJob to have a callback function");
if (this.arguments.trySwap()) |arguments| {
- const count = arguments.getLengthOfArray(globalThis);
- if (count > 0) {
- if (count > args_buf.len) {
- args = bun.default_allocator.alloc(JSC.JSValue, count) catch unreachable;
- args_needs_deinit = true;
- } else {
- args = args_buf[0..count];
- }
- var arg = args.ptr;
- var i: u32 = 0;
- while (i < count) : (i += 1) {
- arg[0] = JSC.JSObject.getIndex(arguments, globalThis, @truncate(u32, i));
- arg += 1;
+ // Bun.sleep passes a Promise
+ if (arguments.jsType() == .JSPromise) {
+ args_buf[0] = arguments;
+ args = args_buf[0..1];
+ } else {
+ const count = arguments.getLengthOfArray(globalThis);
+ if (count > 0) {
+ if (count > args_buf.len) {
+ args = bun.default_allocator.alloc(JSC.JSValue, count) catch unreachable;
+ args_needs_deinit = true;
+ } else {
+ args = args_buf[0..count];
+ }
+ var arg = args.ptr;
+ var i: u32 = 0;
+ while (i < count) : (i += 1) {
+ arg[0] = JSC.JSObject.getIndex(arguments, globalThis, @truncate(u32, i));
+ arg += 1;
+ }
}
}
}
@@ -2916,7 +2922,7 @@ pub const Timer = struct {
this.deinit();
// get the value out of the promise
- _ = promise.result(this.globalThis.vm());
+ _ = promise.result(globalThis.vm());
},
.Pending => {
result.then(globalThis, this, CallbackJob__onResolve, CallbackJob__onReject);
diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp
index 10002b664..2ce07617a 100644
--- a/src/bun.js/bindings/JSBuffer.cpp
+++ b/src/bun.js/bindings/JSBuffer.cpp
@@ -580,14 +580,14 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J
}
auto castedThisValue = callFrame->uncheckedArgument(0);
- JSC::JSUint8Array* castedThis = JSC::jsDynamicCast<JSC::JSUint8Array*>(castedThisValue);
+ JSC::JSArrayBufferView* castedThis = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(castedThisValue);
if (UNLIKELY(!castedThis)) {
throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (first argument)"_s);
return JSValue::encode(jsUndefined());
}
auto buffer = callFrame->uncheckedArgument(1);
- JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ JSC::JSArrayBufferView* view = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(buffer);
if (UNLIKELY(!view)) {
throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (2nd argument)"_s);
return JSValue::encode(jsUndefined());
@@ -657,8 +657,8 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J
auto targetLength = targetEnd - targetStart;
auto actualLength = std::min(sourceLength, targetLength);
- auto sourceStartPtr = castedThis->typedVector() + sourceStart;
- auto targetStartPtr = view->typedVector() + targetStart;
+ auto sourceStartPtr = reinterpret_cast<unsigned char*>(castedThis->vector()) + sourceStart;
+ auto targetStartPtr = reinterpret_cast<unsigned char*>(view->vector()) + targetStart;
auto result = actualLength > 0 ? memcmp(sourceStartPtr, targetStartPtr, actualLength) : 0;
@@ -729,7 +729,10 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_concatBody(JSC::JS
static inline JSC::EncodedJSValue jsBufferConstructorFunction_isEncodingBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
- auto encoding_ = callFrame->argument(0).toString(lexicalGlobalObject);
+ auto* encoding_ = callFrame->argument(0).toStringOrNull(lexicalGlobalObject);
+ if (!encoding_)
+ return JSValue::encode(jsBoolean(false));
+
std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, encoding_);
return JSValue::encode(jsBoolean(!!encoded));
}
@@ -955,7 +958,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_equalsBody(JSC::JSGl
}
auto buffer = callFrame->uncheckedArgument(0);
- JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ JSC::JSArrayBufferView* view = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(buffer);
if (UNLIKELY(!view)) {
throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer"_s);
return JSValue::encode(jsUndefined());
@@ -969,7 +972,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_equalsBody(JSC::JSGl
size_t a_length = castedThis->byteLength();
size_t b_length = view->byteLength();
auto sourceStartPtr = castedThis->typedVector();
- auto targetStartPtr = view->typedVector();
+ auto targetStartPtr = reinterpret_cast<unsigned char*>(view->vector());
// same pointer, same length, same contents
if (sourceStartPtr == targetStartPtr && a_length == b_length)
@@ -1347,6 +1350,73 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap64Body(JSC::JSGl
return JSC::JSValue::encode(castedThis);
}
+static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSUint8Array* castedThis, size_t offset, size_t length, WebCore::BufferEncodingType encoding)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (UNLIKELY(length == 0)) {
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsEmptyString(vm)));
+ }
+
+ JSC::EncodedJSValue ret = 0;
+
+ switch (encoding) {
+ case WebCore::BufferEncodingType::latin1: {
+ LChar* data = nullptr;
+ auto str = String::createUninitialized(length, data);
+ memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
+ return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
+ }
+
+ case WebCore::BufferEncodingType::ucs2:
+ case WebCore::BufferEncodingType::utf16le: {
+ UChar* data = nullptr;
+ size_t u16length = length / 2;
+ if (u16length == 0) {
+ return JSC::JSValue::encode(JSC::jsEmptyString(vm));
+ } else {
+ auto str = String::createUninitialized(u16length, data);
+ // always zero out the last byte of the string incase the buffer is not a multiple of 2
+ data[u16length - 1] = 0;
+ memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
+ return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
+ }
+
+ break;
+ }
+
+ case WebCore::BufferEncodingType::ascii: {
+ // ascii: we always know the length
+ // so we might as well allocate upfront
+ LChar* data = nullptr;
+ auto str = String::createUninitialized(length, data);
+ Bun__encoding__writeLatin1(castedThis->typedVector() + offset, length, data, length, static_cast<uint8_t>(encoding));
+ return JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
+ }
+
+ case WebCore::BufferEncodingType::buffer:
+ case WebCore::BufferEncodingType::utf8:
+ case WebCore::BufferEncodingType::base64:
+ case WebCore::BufferEncodingType::base64url:
+ case WebCore::BufferEncodingType::hex: {
+ ret = Bun__encoding__toString(castedThis->typedVector() + offset, length, lexicalGlobalObject, static_cast<uint8_t>(encoding));
+ break;
+ }
+ default: {
+ throwTypeError(lexicalGlobalObject, scope, "Unsupported encoding? This shouldn't happen"_s);
+ break;
+ }
+ }
+
+ JSC::JSValue retValue = JSC::JSValue::decode(ret);
+ if (UNLIKELY(!retValue.isString())) {
+ scope.throwException(lexicalGlobalObject, retValue);
+ return JSC::JSValue::encode(jsUndefined());
+ }
+
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(retValue));
+}
+
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
@@ -1358,8 +1428,6 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
if (length == 0)
return JSC::JSValue::encode(JSC::jsEmptyString(vm));
- auto scope = DECLARE_THROW_SCOPE(vm);
-
switch (callFrame->argumentCount()) {
case 0: {
break;
@@ -1371,6 +1439,8 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
if (arg1.value().isString()) {
std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, arg1.value());
if (!encoded) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s);
return JSC::JSValue::encode(jsUndefined());
}
@@ -1386,6 +1456,8 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
JSC::JSValue arg2 = callFrame->uncheckedArgument(1);
int32_t ioffset = arg2.toInt32(lexicalGlobalObject);
if (ioffset < 0) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
throwTypeError(lexicalGlobalObject, scope, "Offset must be a positive integer"_s);
return JSC::JSValue::encode(jsUndefined());
}
@@ -1403,61 +1475,33 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
length -= std::min(offset, length);
- if (UNLIKELY(length == 0)) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsEmptyString(vm)));
- }
+ return jsBufferToString(vm, lexicalGlobalObject, castedThis, offset, length, encoding);
+}
- JSC::EncodedJSValue ret = 0;
+// DOMJIT makes it slower! TODO: investigate why
+// JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsBufferPrototypeToStringWithoutTypeChecks, JSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::JSUint8Array* thisValue, JSC::JSString* encodingValue));
- switch (encoding) {
- case WebCore::BufferEncodingType::latin1: {
- LChar* data = nullptr;
- auto str = String::createUninitialized(length, data);
- memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
- ret = JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
- break;
- }
+// JSC_DEFINE_JIT_OPERATION(jsBufferPrototypeToStringWithoutTypeChecks, JSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSUint8Array* thisValue, JSString* encodingValue))
+// {
+// VM& vm = JSC::getVM(lexicalGlobalObject);
+// IGNORE_WARNINGS_BEGIN("frame-address")
+// CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+// IGNORE_WARNINGS_END
+// JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
- case WebCore::BufferEncodingType::ucs2:
- case WebCore::BufferEncodingType::utf16le: {
- UChar* data = nullptr;
- size_t u16length = length / 2;
- if (u16length == 0) {
- ret = JSC::JSValue::encode(JSC::jsEmptyString(vm));
- } else {
- auto str = String::createUninitialized(u16length, data);
- // always zero out the last byte of the string incase the buffer is not a multiple of 2
- data[u16length - 1] = 0;
- memcpy(data, reinterpret_cast<const char*>(castedThis->typedVector() + offset), length);
- ret = JSC::JSValue::encode(JSC::jsString(vm, WTFMove(str)));
- }
+// std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, encodingValue);
+// if (!encoded) {
+// auto scope = DECLARE_THROW_SCOPE(vm);
- break;
- }
+// throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s);
+// return {};
+// }
- case WebCore::BufferEncodingType::buffer:
- case WebCore::BufferEncodingType::utf8:
- case WebCore::BufferEncodingType::ascii:
- case WebCore::BufferEncodingType::base64:
- case WebCore::BufferEncodingType::base64url:
- case WebCore::BufferEncodingType::hex: {
- ret = Bun__encoding__toString(castedThis->typedVector() + offset, length, lexicalGlobalObject, static_cast<uint8_t>(encoding));
- break;
- }
- default: {
- throwTypeError(lexicalGlobalObject, scope, "Unsupported encoding? This shouldn't happen"_s);
- break;
- }
- }
+// auto encoding = encoded.value();
- JSC::JSValue retValue = JSC::JSValue::decode(ret);
- if (UNLIKELY(!retValue.isString())) {
- scope.throwException(lexicalGlobalObject, retValue);
- return JSC::JSValue::encode(jsUndefined());
- }
+// return JSValue::decode(jsBufferToString(vm, lexicalGlobalObject, thisValue, 0, thisValue->byteLength(), encoding));
+// }
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(retValue));
-}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
diff --git a/src/bun.js/bindings/JSBufferEncodingType.cpp b/src/bun.js/bindings/JSBufferEncodingType.cpp
index d65e90d2b..8d99dd4db 100644
--- a/src/bun.js/bindings/JSBufferEncodingType.cpp
+++ b/src/bun.js/bindings/JSBufferEncodingType.cpp
@@ -30,19 +30,21 @@
namespace WebCore {
using namespace JSC;
+static const NeverDestroyed<String> values[] = {
+ MAKE_STATIC_STRING_IMPL("utf8"),
+ MAKE_STATIC_STRING_IMPL("ucs2"),
+ MAKE_STATIC_STRING_IMPL("utf16le"),
+ MAKE_STATIC_STRING_IMPL("latin1"),
+ MAKE_STATIC_STRING_IMPL("ascii"),
+ MAKE_STATIC_STRING_IMPL("base64"),
+ MAKE_STATIC_STRING_IMPL("base64url"),
+ MAKE_STATIC_STRING_IMPL("hex"),
+ MAKE_STATIC_STRING_IMPL("buffer"),
+};
+
String convertEnumerationToString(BufferEncodingType enumerationValue)
{
- static const NeverDestroyed<String> values[] = {
- MAKE_STATIC_STRING_IMPL("utf8"),
- MAKE_STATIC_STRING_IMPL("ucs2"),
- MAKE_STATIC_STRING_IMPL("utf16le"),
- MAKE_STATIC_STRING_IMPL("latin1"),
- MAKE_STATIC_STRING_IMPL("ascii"),
- MAKE_STATIC_STRING_IMPL("base64"),
- MAKE_STATIC_STRING_IMPL("base64url"),
- MAKE_STATIC_STRING_IMPL("hex"),
- MAKE_STATIC_STRING_IMPL("buffer"),
- };
+
ASSERT(static_cast<size_t>(enumerationValue) < std::size(values));
return values[static_cast<size_t>(enumerationValue)];
}
@@ -55,9 +57,9 @@ template<> JSString* convertEnumerationToJS(JSGlobalObject& lexicalGlobalObject,
// this function is mostly copied from node
template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType>(JSGlobalObject& lexicalGlobalObject, JSValue value)
{
-
- JSC::JSString* str = value.toStringOrNull(&lexicalGlobalObject);
- if (!str)
+ // caller must check if value is a string
+ JSC::JSString* str = asString(value);
+ if (UNLIKELY(!str))
return std::nullopt;
auto encoding = str->value(&lexicalGlobalObject);
@@ -75,21 +77,7 @@ template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType
switch (encoding[0]) {
case 'u':
- case 'U':
- // utf8, utf16le
- if (encoding[1] == 't' && encoding[2] == 'f') {
- // Skip `-`
- const size_t skip = encoding[3] == '-' ? 4 : 3;
- if (encoding[skip] == '8' && encoding[skip + 1] == '\0')
- return BufferEncodingType::utf8;
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(skip, 5), "16le"_s))
- return BufferEncodingType::ucs2;
- // ucs2
- } else if (encoding[1] == 'c' && encoding[2] == 's') {
- const size_t skip = encoding[3] == '-' ? 4 : 3;
- if (encoding[skip] == '2' && encoding[skip + 1] == '\0')
- return BufferEncodingType::ucs2;
- }
+ case 'U': {
if (WTF::equalIgnoringASCIICase(encoding, "utf8"_s))
return BufferEncodingType::utf8;
if (WTF::equalIgnoringASCIICase(encoding, "utf-8"_s))
@@ -103,35 +91,17 @@ template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType
if (WTF::equalIgnoringASCIICase(encoding, "utf-16le"_s))
return BufferEncodingType::ucs2;
break;
+ }
case 'l':
- case 'L':
- // latin1
- if (encoding[1] == 'a') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 4), "tin1"_s))
- return BufferEncodingType::latin1;
- }
+ case 'L': {
if (WTF::equalIgnoringASCIICase(encoding, "latin1"_s))
return BufferEncodingType::latin1;
break;
+ }
case 'b':
- case 'B':
- // binary is a deprecated alias of latin1
- if (encoding[1] == 'i') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 5), "nary"_s))
- return BufferEncodingType::latin1;
- // buffer
- } else if (encoding[1] == 'u') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 5), "ffer"_s))
- return BufferEncodingType::buffer;
- // base64
- } else if (encoding[1] == 'a') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 5), "se64"_s))
- return BufferEncodingType::base64;
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 8), "se64url"_s))
- return BufferEncodingType::base64url;
- }
+ case 'B': {
if (WTF::equalIgnoringASCIICase(encoding, "binary"_s))
return BufferEncodingType::latin1; // BINARY is a deprecated alias of LATIN1.
if (WTF::equalIgnoringASCIICase(encoding, "buffer"_s))
@@ -141,15 +111,12 @@ template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType
if (WTF::equalIgnoringASCIICase(encoding, "base64url"_s))
return BufferEncodingType::base64url;
break;
+ }
case 'a':
case 'A':
// ascii
- if (encoding[1] == 's') {
- if (WTF::equalIgnoringASCIICase(encoding.substringSharingImpl(2, 3), "cii"_s))
- return BufferEncodingType::ascii;
- }
- if (WTF::equalIgnoringASCIICase(encoding, "ascii"_s))
+ if (WTF::equalLettersIgnoringASCIICase(encoding, "ascii"_s))
return BufferEncodingType::ascii;
break;
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index bfd775cde..54ccba343 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -108,6 +108,7 @@
#include "ModuleLoader.h"
#include "ZigGeneratedClasses.h"
+#include "JavaScriptCore/DateInstance.h"
#include "BunPlugin.h"
@@ -713,7 +714,7 @@ JSC_DEFINE_CUSTOM_GETTER(lazyProcessEnvGetter,
globalObject->processEnvObject());
}
-static JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
+JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
JSC::VM& vm = globalObject->vm();
@@ -741,7 +742,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask,
return JSC::JSValue::encode(JSC::jsUndefined());
}
-static JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
+JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
JSC::VM& vm = globalObject->vm();
@@ -790,6 +791,45 @@ static JSC_DEFINE_HOST_FUNCTION(functionSetTimeout,
return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num), JSValue::encode(arguments));
}
+JSC_DEFINE_HOST_FUNCTION(functionBunSleepThenCallback,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ JSC::VM& vm = globalObject->vm();
+
+ RELEASE_ASSERT(callFrame->argumentCount() == 1);
+ JSPromise* promise = jsCast<JSC::JSPromise*>(callFrame->argument(0));
+ RELEASE_ASSERT(promise);
+
+ promise->resolve(globalObject, JSC::jsUndefined());
+
+ return JSC::JSValue::encode(promise);
+}
+
+JSC_DEFINE_HOST_FUNCTION(functionBunSleep,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ JSC::VM& vm = globalObject->vm();
+
+ JSC::JSValue millisecondsValue = callFrame->argument(0);
+
+ if (millisecondsValue.inherits<JSC::DateInstance>()) {
+ auto now = MonotonicTime::now();
+ auto milliseconds = jsCast<JSC::DateInstance*>(millisecondsValue)->internalNumber() - now.approximateWallTime().secondsSinceEpoch().milliseconds();
+ millisecondsValue = JSC::jsNumber(milliseconds > 0 ? milliseconds : 0);
+ }
+
+ if (!millisecondsValue.isNumber()) {
+ auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ JSC::throwTypeError(globalObject, scope, "sleep expects a number (milliseconds)"_s);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ Zig::GlobalObject* global = JSC::jsCast<Zig::GlobalObject*>(globalObject);
+ JSC::JSPromise* promise = JSC::JSPromise::create(vm, globalObject->promiseStructure());
+ Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(global->bunSleepThenCallback()), JSC::JSValue::encode(millisecondsValue), JSValue::encode(promise));
+ return JSC::JSValue::encode(promise);
+}
+
static JSC_DEFINE_HOST_FUNCTION(functionSetInterval,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
@@ -2490,6 +2530,11 @@ void GlobalObject::finishCreation(VM& vm)
init.set(JSFunction::create(init.vm, init.owner, 4, "emitReadable"_s, WebCore::jsReadable_emitReadable_, ImplementationVisibility::Public));
});
+ m_bunSleepThenCallback.initLater(
+ [](const Initializer<JSFunction>& init) {
+ init.set(JSFunction::create(init.vm, init.owner, 1, "onSleep"_s, functionBunSleepThenCallback, ImplementationVisibility::Public));
+ });
+
m_performMicrotaskVariadicFunction.initLater(
[](const Initializer<JSFunction>& init) {
init.set(JSFunction::create(init.vm, init.owner, 4, "performMicrotaskVariadic"_s, jsFunctionPerformMicrotaskVariadic, ImplementationVisibility::Public));
@@ -3344,6 +3389,13 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
{
+ JSC::Identifier identifier = JSC::Identifier::fromString(vm, "sleep"_s);
+ object->putDirectNativeFunction(vm, this, identifier, 1, functionBunSleep, ImplementationVisibility::Public, NoIntrinsic,
+ JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ }
+
+ {
+
JSC::Identifier identifier = JSC::Identifier::fromString(vm, "env"_s);
object->putDirectCustomAccessor(vm, identifier,
JSC::CustomGetterSetter::create(vm, lazyProcessEnvGetter, lazyProcessEnvSetter),
@@ -3529,6 +3581,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_requireResolveFunctionStructure.visit(visitor);
thisObject->m_resolveFunctionPrototype.visit(visitor);
thisObject->m_dnsObject.visit(visitor);
+ thisObject->m_bunSleepThenCallback.visit(visitor);
for (auto& barrier : thisObject->m_thenables) {
visitor.append(barrier);
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index f2364fd61..2b688f09d 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -247,6 +247,8 @@ public:
Structure* requireResolveFunctionStructure() { return m_requireResolveFunctionStructure.getInitializedOnMainThread(this); }
JSObject* requireResolveFunctionPrototype() { return m_resolveFunctionPrototype.getInitializedOnMainThread(this); }
+ JSFunction* bunSleepThenCallback() { return m_bunSleepThenCallback.getInitializedOnMainThread(this); }
+
JSObject* dnsObject() { return m_dnsObject.getInitializedOnMainThread(this); }
JSC::JSObject* processObject()
@@ -453,6 +455,7 @@ private:
LazyProperty<JSGlobalObject, JSC::Structure> m_requireResolveFunctionStructure;
LazyProperty<JSGlobalObject, JSObject> m_resolveFunctionPrototype;
LazyProperty<JSGlobalObject, JSObject> m_dnsObject;
+ LazyProperty<JSGlobalObject, JSFunction> m_bunSleepThenCallback;
DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock);
void* m_bunVM;
diff --git a/src/bun.js/crypto.exports.js b/src/bun.js/crypto.exports.js
index fb38a9352..6f9e82b5d 100644
--- a/src/bun.js/crypto.exports.js
+++ b/src/bun.js/crypto.exports.js
@@ -6,6 +6,8 @@ var __getProtoOf = Object.getPrototypeOf,
__hasOwnProp = Object.prototype.hasOwnProperty;
var __require = id => import.meta.require(id);
+const crypto = globalThis.crypto;
+const globalCrypto = crypto;
var __esm = (fn, res) =>
function () {
@@ -89,7 +91,7 @@ var require_browser = __commonJS({
Use Chrome, Firefox or Internet Explorer 11`);
}
var Buffer2 = require_safe_buffer().Buffer,
- crypto2 = global.crypto || global.msCrypto;
+ crypto2 = globalCrypto;
crypto2 && crypto2.getRandomValues ? (module.exports = randomBytes) : (module.exports = oldBrowser);
function randomBytes(size, cb) {
if (size > MAX_UINT32) throw new RangeError("requested too many random bytes");
@@ -1588,7 +1590,7 @@ var require_async = __commonJS({
sync = require_sync_browser(),
toBuffer = require_to_buffer(),
ZERO_BUF,
- subtle = global.crypto && global.crypto.subtle,
+ subtle = globalCrypto.subtle,
toBrowser = {
sha: "SHA-1",
"sha-1": "SHA-1",
@@ -23553,15 +23555,10 @@ var require_browser10 = __commonJS({
var require_browser11 = __commonJS({
"node_modules/randomfill/browser.js"(exports) {
"use strict";
- function oldBrowser() {
- throw new Error(`secure random number generation not supported by this browser
-use chrome, FireFox or Internet Explorer 11`);
- }
var safeBuffer = require_safe_buffer(),
randombytes = require_browser(),
Buffer2 = safeBuffer.Buffer,
kBufferMaxLength = safeBuffer.kMaxLength,
- crypto2 = global.crypto || global.msCrypto,
kMaxUint32 = Math.pow(2, 32) - 1;
function assertOffset(offset, length) {
if (typeof offset != "number" || offset !== offset) throw new TypeError("offset must be a number");
@@ -23654,6 +23651,7 @@ var require_crypto_browserify2 = __commonJS({
exports.privateEncrypt = publicEncrypt.privateEncrypt;
exports.publicDecrypt = publicEncrypt.publicDecrypt;
exports.privateDecrypt = publicEncrypt.privateDecrypt;
+ exports.getRandomValues = values => crypto.getRandomValues(values);
var rf = require_browser11();
exports.randomFill = rf.randomFill;
exports.randomFillSync = rf.randomFillSync;
@@ -23692,15 +23690,6 @@ var crypto_exports = {
...require_crypto_browserify2(),
[Symbol.for("CommonJS")]: 0,
};
-__export(crypto_exports, {
- DEFAULT_ENCODING: () => DEFAULT_ENCODING,
- getRandomValues: () => getRandomValues,
- randomUUID: () => randomUUID,
- scrypt: () => scrypt,
- scryptSync: () => scryptSync,
- timingSafeEqual: () => timingSafeEqual,
- webcrypto: () => webcrypto,
-});
var DEFAULT_ENCODING = "buffer",
getRandomValues = array => crypto.getRandomValues(array),
randomUUID = () => crypto.randomUUID(),
@@ -23754,6 +23743,17 @@ timingSafeEqual &&
value: "::bunternal::",
}));
var webcrypto = crypto;
+__export(crypto_exports, {
+ DEFAULT_ENCODING: () => DEFAULT_ENCODING,
+ getRandomValues: () => getRandomValues,
+ randomUUID: () => randomUUID,
+ scrypt: () => scrypt,
+ scryptSync: () => scryptSync,
+ timingSafeEqual: () => timingSafeEqual,
+ webcrypto: () => webcrypto,
+ subtle: () => webcrypto.subtle,
+});
+
export const {
randomBytes,
rng,
diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig
index 525ee991b..f7a91393e 100644
--- a/src/bun.js/node/node_os.zig
+++ b/src/bun.js/node/node_os.zig
@@ -54,11 +54,147 @@ pub const Os = struct {
return JSC.ZigString.init(Global.arch_name).withEncoding().toValue(globalThis);
}
+ const CPU = struct {
+ model: JSC.ZigString = JSC.ZigString.init("unknown"),
+ speed: u64 = 0,
+ times: struct {
+ user: u64 = 0,
+ nice: u64 = 0,
+ sys: u64 = 0,
+ idle: u64 = 0,
+ irq: u64 = 0,
+ } = .{}
+ };
+
pub fn cpus(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
JSC.markBinding(@src());
- // TODO:
- return JSC.JSArray.from(globalThis, &.{});
+ var cpu_buffer: [8192]CPU = undefined;
+ const cpus_or_error = if (comptime Environment.isLinux)
+ cpusImplLinux(&cpu_buffer)
+ else
+ @as(anyerror![]CPU, cpu_buffer[0..0]); // unsupported platform -> empty array
+
+ if (cpus_or_error) |list| {
+ // Convert the CPU list to a JS Array
+ const values = JSC.JSValue.createEmptyArray(globalThis, list.len);
+ for (list) |cpu, cpu_index| {
+ const obj = JSC.JSValue.createEmptyObject(globalThis, 3);
+ obj.put(globalThis, JSC.ZigString.static("model"), cpu.model.withEncoding().toValueGC(globalThis));
+ obj.put(globalThis, JSC.ZigString.static("speed"), JSC.JSValue.jsNumberFromUint64(cpu.speed));
+
+ const timesFields = comptime std.meta.fieldNames(@TypeOf(cpu.times));
+ const times = JSC.JSValue.createEmptyObject(globalThis, 5);
+ inline for (timesFields) |fieldName| {
+ times.put(globalThis, JSC.ZigString.static(fieldName),
+ JSC.JSValue.jsNumberFromUint64(@field(cpu.times, fieldName)));
+ }
+ obj.put(globalThis, JSC.ZigString.static("times"), times);
+ values.putIndex(globalThis, @intCast(u32, cpu_index), obj);
+ }
+ return values;
+
+ } else |zig_err| {
+ const msg = switch (zig_err) {
+ error.too_many_cpus => "Too many CPUs or malformed /proc/cpuinfo file",
+ error.eol => "Malformed /proc/stat file",
+ else => "An error occurred while fetching cpu information",
+ };
+ //TODO more suitable error type?
+ const err = JSC.SystemError{
+ .message = JSC.ZigString.init(msg),
+ };
+ globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis));
+ return JSC.JSValue.jsUndefined();
+ }
+ }
+
+ fn cpusImplLinux(cpu_buffer: []CPU) ![]CPU {
+ // Use a large line buffer because the /proc/stat file can have a very long list of interrupts
+ var line_buffer: [1024*8]u8 = undefined;
+ var num_cpus: usize = 0;
+
+ // Read /proc/stat to get number of CPUs and times
+ if (std.fs.openFileAbsolute("/proc/stat", .{})) |file| {
+ defer file.close();
+ var reader = file.reader();
+
+ // Skip the first line (aggregate of all CPUs)
+ try reader.skipUntilDelimiterOrEof('\n');
+
+ // Read each CPU line
+ while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
+
+ if (num_cpus >= cpu_buffer.len) return error.too_many_cpus;
+
+ // CPU lines are formatted as `cpu0 user nice sys idle iowait irq softirq`
+ var toks = std.mem.tokenize(u8, line, " \t");
+ const cpu_name = toks.next();
+ if (cpu_name == null or !std.mem.startsWith(u8, cpu_name.?, "cpu")) break; // done with CPUs
+
+ // Default initialize the CPU to ensure that we never return uninitialized fields
+ cpu_buffer[num_cpus] = CPU{};
+
+ //NOTE: libuv assumes this is fixed on Linux, not sure that's actually the case
+ const scale = 10;
+ cpu_buffer[num_cpus].times.user = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ cpu_buffer[num_cpus].times.nice = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ cpu_buffer[num_cpus].times.sys = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ cpu_buffer[num_cpus].times.idle = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+ _ = try (toks.next() orelse error.eol); // skip iowait
+ cpu_buffer[num_cpus].times.irq = scale * try std.fmt.parseInt(u64, toks.next() orelse return error.eol, 10);
+
+ num_cpus += 1;
+ }
+ } else |_| {
+ return error.cannot_open_proc_stat;
+ }
+
+ const slice = cpu_buffer[0..num_cpus];
+
+ // Read /proc/cpuinfo to get model information (optional)
+ if (std.fs.openFileAbsolute("/proc/cpuinfo", .{})) |file| {
+ defer file.close();
+ var reader = file.reader();
+ const key_processor = "processor\t: ";
+ const key_model_name = "model name\t: ";
+
+ var cpu_index: usize = 0;
+ while (try reader.readUntilDelimiterOrEof(&line_buffer, '\n')) |line| {
+
+ if (std.mem.startsWith(u8, line, key_processor)) {
+ // If this line starts a new processor, parse the index from the line
+ const digits = std.mem.trim(u8, line[key_processor.len..], " \t\n");
+ cpu_index = try std.fmt.parseInt(usize, digits, 10);
+ if (cpu_index >= slice.len) return error.too_may_cpus;
+
+ } else if (std.mem.startsWith(u8, line, key_model_name)) {
+ // If this is the model name, extract it and store on the current cpu
+ const model_name = line[key_model_name.len..];
+ slice[cpu_index].model = JSC.ZigString.init(model_name);
+ }
+ //TODO: special handling for ARM64 (no model name)?
+ }
+ } else |_| {
+ // Do nothing: CPU default initializer has set model name to "unknown"
+ }
+
+ // Read /sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq to get current frequency (optional)
+ for (slice) |*cpu, cpu_index| {
+ var path_buf: [128]u8 = undefined;
+ const path = try std.fmt.bufPrint(&path_buf, "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", .{cpu_index});
+ if (std.fs.openFileAbsolute(path, .{})) |file| {
+ defer file.close();
+
+ const bytes_read = try file.readAll(&line_buffer);
+ const digits = std.mem.trim(u8, line_buffer[0..bytes_read], " \n");
+ cpu.speed = try std.fmt.parseInt(u64, digits, 10) / 1000;
+ } else |_| {
+ // Do nothing: CPU default initializer has set speed to 0
+ }
+ }
+
+ return slice;
}
pub fn endianness(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue {
diff --git a/src/bun.js/perf_hooks.exports.js b/src/bun.js/perf_hooks.exports.js
index 8a1a9a915..c461abafe 100644
--- a/src/bun.js/perf_hooks.exports.js
+++ b/src/bun.js/perf_hooks.exports.js
@@ -22,4 +22,5 @@ export default {
PerformanceEntry,
PerformanceEntry,
PerformanceNodeTiming,
+ [Symbol.for("CommonJS")]: 0,
};
diff --git a/src/bun.js/util.exports.js b/src/bun.js/util.exports.js
index 1bba01977..c0adc0344 100644
--- a/src/bun.js/util.exports.js
+++ b/src/bun.js/util.exports.js
@@ -27,8 +27,11 @@ var require_inherits_browser = __commonJS({
};
},
});
-
-const exports = {};
+const deepEquals = Bun.deepEquals;
+const isDeepStrictEqual = (a, b) => deepEquals(a, b, true);
+const exports = {
+ isDeepStrictEqual,
+};
var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
var formatRegExp = /%[sdj%]/g;
function format(f) {
@@ -576,4 +579,5 @@ export {
inherits,
promisify,
callbackify,
+ isDeepStrictEqual,
};
diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig
index 6729cc4de..8a6e3224b 100644
--- a/src/bun.js/webcore/encoding.zig
+++ b/src/bun.js/webcore/encoding.zig
@@ -795,15 +795,19 @@ pub const Encoder = struct {
switch (comptime encoding) {
.ascii => {
- var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global);
-
- @memcpy(to.ptr, input_ptr, to.len);
+ if (bun.simdutf.validate.ascii(input)) {
+ return ZigString.init(input).toValueGC(global);
+ }
- // Hoping this gets auto vectorized
- for (to[0..to.len]) |c, i| {
- to[i] = @as(u8, @truncate(u7, c));
+ if (input.len < 512) {
+ var buf: [512]u8 = undefined;
+ var to = buf[0..input.len];
+ strings.copyLatin1IntoASCII(to, input);
+ return ZigString.init(to).toValueGC(global);
}
+ var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global);
+ strings.copyLatin1IntoASCII(to, input);
return ZigString.init(to).toExternalValue(global);
},
.latin1 => {
@@ -857,7 +861,7 @@ pub const Encoder = struct {
}
}
- pub fn writeU8(input: [*]const u8, len: usize, to: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding) i64 {
+ pub fn writeU8(input: [*]const u8, len: usize, to_ptr: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding) i64 {
if (len == 0 or to_len == 0)
return 0;
@@ -871,39 +875,42 @@ pub const Encoder = struct {
switch (comptime encoding) {
JSC.Node.Encoding.buffer => {
const written = @min(len, to_len);
- @memcpy(to, input, written);
+ @memcpy(to_ptr, input, written);
return @intCast(i64, written);
},
.latin1, .ascii => {
const written = @min(len, to_len);
- @memcpy(to, input, written);
- // Hoping this gets auto vectorized
- for (to[0..written]) |c, i| {
- to[i] = @as(u8, @truncate(u7, c));
+ var to = to_ptr[0..written];
+ var remain = input[0..written];
+
+ if (bun.simdutf.validate.ascii(remain)) {
+ @memcpy(to.ptr, remain.ptr, written);
+ } else {
+ strings.copyLatin1IntoASCII(to, remain);
}
return @intCast(i64, written);
},
.utf8 => {
// need to encode
- return @intCast(i64, strings.copyLatin1IntoUTF8(to[0..to_len], []const u8, input[0..len]).written);
+ return @intCast(i64, strings.copyLatin1IntoUTF8(to_ptr[0..to_len], []const u8, input[0..len]).written);
},
// encode latin1 into UTF16
JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => {
if (to_len < 2)
return 0;
- if (std.mem.isAligned(@ptrToInt(to), @alignOf([*]u16))) {
+ if (std.mem.isAligned(@ptrToInt(to_ptr), @alignOf([*]u16))) {
var buf = input[0..len];
- var output = @ptrCast([*]u16, @alignCast(@alignOf(u16), to))[0 .. to_len / 2];
+ var output = @ptrCast([*]u16, @alignCast(@alignOf(u16), to_ptr))[0 .. to_len / 2];
var written = strings.copyLatin1IntoUTF16([]u16, output, []const u8, buf).written;
return written * 2;
} else {
var buf = input[0..len];
- var output = @ptrCast([*]align(1) u16, to)[0 .. to_len / 2];
+ var output = @ptrCast([*]align(1) u16, to_ptr)[0 .. to_len / 2];
var written = strings.copyLatin1IntoUTF16([]align(1) u16, output, []const u8, buf).written;
return written * 2;
@@ -911,7 +918,7 @@ pub const Encoder = struct {
},
JSC.Node.Encoding.hex => {
- return @intCast(i64, strings.decodeHexToBytes(to[0..to_len], u8, input[0..len]));
+ return @intCast(i64, strings.decodeHexToBytes(to_ptr[0..to_len], u8, input[0..len]));
},
JSC.Node.Encoding.base64url => {
@@ -919,18 +926,18 @@ pub const Encoder = struct {
if (slice.len == 0)
return 0;
- if (strings.eqlComptime(slice[slice.len - 2 ..][0..2], "==")) {
+ if (strings.endsWithComptime(slice, "==")) {
slice = slice[0 .. slice.len - 2];
} else if (slice[slice.len - 1] == '=') {
slice = slice[0 .. slice.len - 1];
}
- const wrote = bun.base64.decodeURLSafe(to[0..to_len], slice).written;
+ const wrote = bun.base64.decodeURLSafe(to_ptr[0..to_len], slice).written;
return @intCast(i64, wrote);
},
JSC.Node.Encoding.base64 => {
- return @intCast(i64, bun.base64.decode(to[0..to_len], input[0..len]).written);
+ return @intCast(i64, bun.base64.decode(to_ptr[0..to_len], input[0..len]).written);
},
// else => return 0,
}
@@ -1094,7 +1101,7 @@ pub const Encoder = struct {
if (slice.len == 0)
return &[_]u8{};
- if (strings.eqlComptime(slice[slice.len - 2 ..][0..2], "==")) {
+ if (strings.endsWithComptime(slice, "==")) {
slice = slice[0 .. slice.len - 2];
} else if (slice[slice.len - 1] == '=') {
slice = slice[0 .. slice.len - 1];
diff --git a/src/node-fallbacks/package-lock.json b/src/node-fallbacks/package-lock.json
index 18307a703..1a5b9a13a 100644
--- a/src/node-fallbacks/package-lock.json
+++ b/src/node-fallbacks/package-lock.json
@@ -23,7 +23,7 @@
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"punycode": "^2.1.1",
- "querystring-es3": "^0.2.1",
+ "querystring-es3": "^1.0.0-0",
"readable-stream": "^4.1.0",
"stream-http": "^3.2.0",
"string_decoder": "^1.3.0",
@@ -1188,11 +1188,24 @@
}
},
"node_modules/querystring-es3": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
- "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "version": "1.0.0-0",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-1.0.0-0.tgz",
+ "integrity": "sha512-0etE6Uj4NYut0/y/6pFgVRDAaFtf6x/n4xnBRL82c8n6yigRBepcgPaBAn2o8EhA9QQR1l8b1Je5s+LsJ+zS+g==",
+ "dependencies": {
+ "buffer": "5.0.5"
+ },
"engines": {
- "node": ">=0.4.x"
+ "node": ">=4"
+ }
+ },
+ "node_modules/querystring-es3/node_modules/buffer": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.5.tgz",
+ "integrity": "sha512-ye69HVxM0Htf+E5YhFLOfHX8dXOkammTDtqCU5xLFMlPYTQkNJ7Kv6I90TjZiPH1yXBHQdRvSAK28EZSwSm3Dg==",
+ "deprecated": "This version of 'buffer' is out-of-date. You must update to v5.0.8 or newer",
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
}
},
"node_modules/randombytes": {
@@ -2275,9 +2288,23 @@
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
},
"querystring-es3": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
- "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
+ "version": "1.0.0-0",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-1.0.0-0.tgz",
+ "integrity": "sha512-0etE6Uj4NYut0/y/6pFgVRDAaFtf6x/n4xnBRL82c8n6yigRBepcgPaBAn2o8EhA9QQR1l8b1Je5s+LsJ+zS+g==",
+ "requires": {
+ "buffer": "5.0.5"
+ },
+ "dependencies": {
+ "buffer": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.5.tgz",
+ "integrity": "sha512-ye69HVxM0Htf+E5YhFLOfHX8dXOkammTDtqCU5xLFMlPYTQkNJ7Kv6I90TjZiPH1yXBHQdRvSAK28EZSwSm3Dg==",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ }
+ }
},
"randombytes": {
"version": "2.1.0",
diff --git a/src/node-fallbacks/package.json b/src/node-fallbacks/package.json
index 66ff79c5b..144f553c0 100644
--- a/src/node-fallbacks/package.json
+++ b/src/node-fallbacks/package.json
@@ -25,7 +25,7 @@
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"punycode": "^2.1.1",
- "querystring-es3": "^0.2.1",
+ "querystring-es3": "^1.0.0-0",
"readable-stream": "^4.1.0",
"stream-http": "^3.2.0",
"string_decoder": "^1.3.0",
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index d24edb99a..775de74d4 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -1085,6 +1085,53 @@ pub inline fn copyU16IntoU8(output_: []u8, comptime InputType: type, input_: Inp
const strings = @This();
+pub fn copyLatin1IntoASCII(dest: []u8, src: []const u8) void {
+ var remain = src;
+ var to = dest;
+
+ const non_ascii_offset = strings.firstNonASCII(remain) orelse @truncate(u32, remain.len);
+ if (non_ascii_offset > 0) {
+ @memcpy(to.ptr, remain.ptr, non_ascii_offset);
+ remain = remain[non_ascii_offset..];
+ to = to[non_ascii_offset..];
+
+ // ascii fast path
+ if (remain.len == 0) {
+ return;
+ }
+ }
+
+ if (to.len >= 16 and bun.Environment.enableSIMD) {
+ const vector_size = 16;
+ // https://zig.godbolt.org/z/qezsY8T3W
+ var remain_in_u64 = remain[0 .. remain.len - (remain.len % vector_size)];
+ var to_in_u64 = to[0 .. to.len - (to.len % vector_size)];
+ var remain_as_u64 = std.mem.bytesAsSlice(u64, remain_in_u64);
+ var to_as_u64 = std.mem.bytesAsSlice(u64, to_in_u64);
+ const end_vector_len = @min(remain_as_u64.len, to_as_u64.len);
+ remain_as_u64 = remain_as_u64[0..end_vector_len];
+ to_as_u64 = to_as_u64[0..end_vector_len];
+ const end_ptr = remain_as_u64.ptr + remain_as_u64.len;
+ // using the pointer instead of the length is super important for the codegen
+ while (end_ptr != remain_as_u64.ptr) {
+ const buf = remain_as_u64[0];
+ // this gets auto-vectorized
+ const mask = @as(u64, 0x7f7f7f7f7f7f7f7f);
+ to_as_u64[0] = buf & mask;
+
+ remain_as_u64 = remain_as_u64[1..];
+ to_as_u64 = to_as_u64[1..];
+ }
+ remain = remain[remain_in_u64.len..];
+ to = to[to_in_u64.len..];
+ }
+
+ for (to) |*to_byte| {
+ to_byte.* = @as(u8, @truncate(u7, remain[0]));
+ remain = remain[1..];
+ }
+}
+
/// Convert a UTF-8 string to a UTF-16 string IF there are any non-ascii characters
/// If there are no non-ascii characters, this returns null
/// This is intended to be used for strings that go to JavaScript
@@ -1096,28 +1143,23 @@ pub fn toUTF16Alloc(allocator: std.mem.Allocator, bytes: []const u8, comptime fa
if (bytes.len == 0)
return &[_]u16{};
use_simdutf: {
- const validated = bun.simdutf.validate.with_errors.ascii(bytes);
- if (validated.status == .success)
+ if (bun.simdutf.validate.ascii(bytes))
return null;
- const offset = @truncate(u32, validated.count);
-
- const trimmed = bun.simdutf.trim.utf8(bytes[offset..]);
+ const trimmed = bun.simdutf.trim.utf8(bytes);
if (trimmed.len == 0)
break :use_simdutf;
const out_length = bun.simdutf.length.utf16.from.utf8.le(trimmed);
- if (out_length != trimmed.len)
+ if (out_length == 0)
break :use_simdutf;
- var out = try allocator.alloc(u16, out_length + offset);
+ var out = try allocator.alloc(u16, out_length);
log("toUTF16 {d} UTF8 -> {d} UTF16", .{ bytes.len, out_length });
- if (offset > 0)
- strings.copyU8IntoU16(out[0..offset], bytes[0..offset]);
- const result = bun.simdutf.convert.utf8.to.utf16.with_errors.le(trimmed, out[offset..]);
+ const result = bun.simdutf.convert.utf8.to.utf16.with_errors.le(trimmed, out);
switch (result.status) {
.success => {
return out;
@@ -1128,7 +1170,7 @@ pub fn toUTF16Alloc(allocator: std.mem.Allocator, bytes: []const u8, comptime fa
return error.InvalidByteSequence;
}
- first_non_ascii = @truncate(u32, result.count) + offset;
+ first_non_ascii = @truncate(u32, result.count);
output_ = std.ArrayList(u16){
.items = out[0..first_non_ascii.?],
.capacity = out.len,
diff --git a/test/bun.js/node-crypto.test.js b/test/bun.js/node-crypto.test.js
index 5d9d6a77d..f148f4fe9 100644
--- a/test/bun.js/node-crypto.test.js
+++ b/test/bun.js/node-crypto.test.js
@@ -21,3 +21,9 @@ it("crypto.createHmac", () => {
expect(result).toBe("bp7ym3X//Ft6uuUn1Y/a2y/kLnIZARl2kXNDBl9Y7Uo=");
});
+
+it("web crypto", async () => {
+ let bytes = new Uint8Array(32);
+ crypto.getRandomValues(bytes);
+ await crypto.subtle.digest("SHA-256", bytes);
+});
diff --git a/test/bun.js/node-dns.test.js b/test/bun.js/node-dns.test.js
index a2780374f..6f4cac22f 100644
--- a/test/bun.js/node-dns.test.js
+++ b/test/bun.js/node-dns.test.js
@@ -53,7 +53,7 @@ test("dns.resolveTxt (txt.socketify.dev)", done => {
test("dns.resolveSoa (bun.sh)", done => {
dns.resolveSoa("bun.sh", (err, result) => {
expect(err).toBeNull();
- expect(result.serial).toBe(2295878541);
+ expect(typeof result.serial).toBe("number");
expect(result.refresh).toBe(10000);
expect(result.retry).toBe(2400);
expect(result.expire).toBe(604800);
diff --git a/test/bun.js/setTimeout.test.js b/test/bun.js/setTimeout.test.js
index 9cd16ece2..393a32bbe 100644
--- a/test/bun.js/setTimeout.test.js
+++ b/test/bun.js/setTimeout.test.js
@@ -89,3 +89,49 @@ it("setTimeout(() => {}, 0)", async () => {
});
expect(ranFirst).toBe(-1);
});
+
+it("Bun.sleep", async () => {
+ var sleeps = 0;
+ await Bun.sleep(0);
+ const start = performance.now();
+ sleeps++;
+ await Bun.sleep(1);
+ sleeps++;
+ await Bun.sleep(2);
+ sleeps++;
+ const end = performance.now();
+ expect((end - start) * 1000).toBeGreaterThanOrEqual(3);
+
+ expect(sleeps).toBe(3);
+});
+
+it("Bun.sleep propagates exceptions", async () => {
+ try {
+ await Bun.sleep(1).then(a => {
+ throw new Error("TestPassed");
+ });
+ throw "Should not reach here";
+ } catch (err) {
+ expect(err.message).toBe("TestPassed");
+ }
+});
+
+it("Bun.sleep works with a Date object", async () => {
+ var ten_ms = new Date();
+ ten_ms.setMilliseconds(ten_ms.getMilliseconds() + 10);
+ const now = performance.now();
+ await Bun.sleep(ten_ms);
+ expect(performance.now() - now).toBeGreaterThanOrEqual(10);
+});
+
+it("node.js timers/promises setTimeout propagates exceptions", async () => {
+ const { setTimeout } = require("timers/promises");
+ try {
+ await setTimeout(1).then(a => {
+ throw new Error("TestPassed");
+ });
+ throw "Should not reach here";
+ } catch (err) {
+ expect(err.message).toBe("TestPassed");
+ }
+});
diff --git a/test/bun.js/text-decoder.test.js b/test/bun.js/text-decoder.test.js
index be3f8421b..da0497464 100644
--- a/test/bun.js/text-decoder.test.js
+++ b/test/bun.js/text-decoder.test.js
@@ -42,7 +42,118 @@ describe("TextDecoder", () => {
gcTrace(true);
expect(decoder.decode(new Uint8Array([0x41, 0x42, 0x43]))).toBe("ABC");
gcTrace(true);
- const result = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33];
+
+ // hit the SIMD code path
+ const result = [
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33,
+ ];
gcTrace(true);
expect(decoder.decode(Uint8Array.from(result))).toBe(String.fromCharCode(...result));
gcTrace(true);
@@ -51,19 +162,16 @@ describe("TextDecoder", () => {
it("should decode unicode text", () => {
const decoder = new TextDecoder();
gcTrace(true);
- var text = `❤️ Red Heart`;
-
- const bytes = [226, 157, 164, 239, 184, 143, 32, 82, 101, 100, 32, 72, 101, 97, 114, 116];
- const decoded = decoder.decode(Uint8Array.from(bytes));
- expect(decoder.encoding).toBe("utf-8");
+ const inputBytes = [226, 157, 164, 239, 184, 143, 32, 82, 101, 100, 32, 72, 101, 97, 114, 116];
+ for (var repeat = 1; repeat < 100; repeat++) {
+ var text = `❤️ Red Heart`.repeat(repeat);
- gcTrace(true);
-
- for (let i = 0; i < text.length; i++) {
- expect(decoded.charCodeAt(i)).toBe(text.charCodeAt(i));
+ var bytes = Array.from({ length: repeat }, () => inputBytes).flat();
+ var decoded = decoder.decode(Uint8Array.from(bytes));
+ expect(decoder.encoding).toBe("utf-8");
+ expect(decoded).toBe(text);
+ gcTrace(true);
}
- expect(decoded).toHaveLength(text.length);
- gcTrace(true);
});
describe("typedArrays", () => {