diff options
author | 2023-06-01 21:16:47 -0400 | |
---|---|---|
committer | 2023-06-01 18:16:47 -0700 | |
commit | 4df1d37ddc54242c339765f22fb90ba2e9e3a99a (patch) | |
tree | d63ede76463e7ecba78a4d4b31e5e8158193552f /src/bun.js/builtins/ts | |
parent | 03ffd1c732aaaa30b5481f197221ce96da559e63 (diff) | |
download | bun-4df1d37ddc54242c339765f22fb90ba2e9e3a99a.tar.gz bun-4df1d37ddc54242c339765f22fb90ba2e9e3a99a.tar.zst bun-4df1d37ddc54242c339765f22fb90ba2e9e3a99a.zip |
Bundle and minify `.exports.js` files. (#3036)
* move all exports.js into src/js
* finalize the sort of this
* and it works
* add test.ts to gitignore
* okay
* convert some to ts just to show
* finish up
* fixup makefile
* minify syntax in dev
* finish rebase
* dont minify all modules
* merge
* finish rebase merge
* flaky test that hangs
Diffstat (limited to 'src/bun.js/builtins/ts')
23 files changed, 0 insertions, 7120 deletions
diff --git a/src/bun.js/builtins/ts/BundlerPlugin.ts b/src/bun.js/builtins/ts/BundlerPlugin.ts deleted file mode 100644 index 831a6614e..000000000 --- a/src/bun.js/builtins/ts/BundlerPlugin.ts +++ /dev/null @@ -1,370 +0,0 @@ -import type { - AnyFunction, - BuildConfig, - BunPlugin, - OnLoadCallback, - OnLoadResult, - OnLoadResultObject, - OnLoadResultSourceCode, - OnResolveCallback, - PluginBuilder, - PluginConstraints, -} from "bun"; - -// This API expects 4 functions: -// It should be generic enough to reuse for Bun.plugin() eventually, too. -interface BundlerPlugin { - onLoad: Map<string, [RegExp, OnLoadCallback][]>; - onResolve: Map<string, [RegExp, OnResolveCallback][]>; - onLoadAsync( - internalID, - sourceCode: string | Uint8Array | ArrayBuffer | DataView | null, - loaderKey: number | null, - ): void; - onResolveAsync(internalID, a, b, c): void; - addError(internalID, error, number): void; - addFilter(filter, namespace, number): void; -} - -// Extra types -type Setup = BunPlugin["setup"]; -type MinifyObj = Exclude<BuildConfig["minify"], boolean>; -interface BuildConfigExt extends BuildConfig { - // we support esbuild-style entryPoints - entryPoints?: string[]; - // plugins is guaranteed to not be null - plugins: BunPlugin[]; -} -interface PluginBuilderExt extends PluginBuilder { - // these functions aren't implemented yet, so we dont publicly expose them - resolve: AnyFunction; - onStart: AnyFunction; - onEnd: AnyFunction; - onDispose: AnyFunction; - // we partially support initialOptions. it's read-only and a subset of - // all options mapped to their esbuild names - initialOptions: any; - // we set this to an empty object - esbuild: any; -} - -export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: BuildConfigExt) { - var onLoadPlugins = new Map<string, [RegExp, AnyFunction][]>(); - var onResolvePlugins = new Map<string, [RegExp, AnyFunction][]>(); - - function validate(filterObject: PluginConstraints, callback, map) { - if (!filterObject || !$isObject(filterObject)) { - throw new TypeError('Expected an object with "filter" RegExp'); - } - - if (!callback || !$isCallable(callback)) { - throw new TypeError("callback must be a function"); - } - - var { filter, namespace = "file" } = filterObject; - - if (!filter) { - throw new TypeError('Expected an object with "filter" RegExp'); - } - - if (!$isRegExpObject(filter)) { - throw new TypeError("filter must be a RegExp"); - } - - if (namespace && !(typeof namespace === "string")) { - throw new TypeError("namespace must be a string"); - } - - if ((namespace?.length ?? 0) === 0) { - namespace = "file"; - } - - if (!/^([/$a-zA-Z0-9_\\-]+)$/.test(namespace)) { - throw new TypeError("namespace can only contain $a-zA-Z0-9_\\-"); - } - - var callbacks = map.$get(namespace); - - if (!callbacks) { - map.$set(namespace, [[filter, callback]]); - } else { - $arrayPush(callbacks, [filter, callback]); - } - } - - function onLoad(filterObject, callback) { - validate(filterObject, callback, onLoadPlugins); - } - - function onResolve(filterObject, callback) { - validate(filterObject, callback, onResolvePlugins); - } - - const processSetupResult = () => { - var anyOnLoad = false, - anyOnResolve = false; - - for (var [namespace, callbacks] of onLoadPlugins.entries()) { - for (var [filter] of callbacks) { - this.addFilter(filter, namespace, 1); - anyOnLoad = true; - } - } - - for (var [namespace, callbacks] of onResolvePlugins.entries()) { - for (var [filter] of callbacks) { - this.addFilter(filter, namespace, 0); - anyOnResolve = true; - } - } - - if (anyOnResolve) { - var onResolveObject = this.onResolve; - if (!onResolveObject) { - this.onResolve = onResolvePlugins; - } else { - for (var [namespace, callbacks] of onResolvePlugins.entries()) { - var existing = onResolveObject.$get(namespace) as [RegExp, AnyFunction][]; - - if (!existing) { - onResolveObject.$set(namespace, callbacks); - } else { - onResolveObject.$set(namespace, existing.concat(callbacks)); - } - } - } - } - - if (anyOnLoad) { - var onLoadObject = this.onLoad; - if (!onLoadObject) { - this.onLoad = onLoadPlugins; - } else { - for (var [namespace, callbacks] of onLoadPlugins.entries()) { - var existing = onLoadObject.$get(namespace) as [RegExp, AnyFunction][]; - - if (!existing) { - onLoadObject.$set(namespace, callbacks); - } else { - onLoadObject.$set(namespace, existing.concat(callbacks)); - } - } - } - } - - return anyOnLoad || anyOnResolve; - }; - - var setupResult = setup({ - config: config, - onDispose: notImplementedIssueFn(2771, "On-dispose callbacks"), - onEnd: notImplementedIssueFn(2771, "On-end callbacks"), - onLoad, - onResolve, - onStart: notImplementedIssueFn(2771, "On-start callbacks"), - resolve: notImplementedIssueFn(2771, "build.resolve()"), - // esbuild's options argument is different, we provide some interop - initialOptions: { - ...config, - bundle: true, - entryPoints: config.entrypoints ?? config.entryPoints ?? [], - minify: typeof config.minify === "boolean" ? config.minify : false, - minifyIdentifiers: config.minify === true || (config.minify as MinifyObj)?.identifiers, - minifyWhitespace: config.minify === true || (config.minify as MinifyObj)?.whitespace, - minifySyntax: config.minify === true || (config.minify as MinifyObj)?.syntax, - outbase: config.root, - platform: config.target === "bun" ? "node" : config.target, - }, - esbuild: {}, - } satisfies PluginBuilderExt as PluginBuilder); - - if (setupResult && $isPromise(setupResult)) { - if ($getPromiseInternalField(setupResult, $promiseFieldFlags) & $promiseStateFulfilled) { - setupResult = $getPromiseInternalField(setupResult, $promiseFieldReactionsOrResult); - } else { - return setupResult.$then(processSetupResult); - } - } - - return processSetupResult(); -} - -export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespace, importer, internalID, kindId) { - // Must be kept in sync with ImportRecord.label - const kind = $ImportKindIdToLabel[kindId]; - - var promiseResult: any = (async (inputPath, inputNamespace, importer, kind) => { - var { onResolve, onLoad } = this; - var results = onResolve.$get(inputNamespace); - if (!results) { - this.onResolveAsync(internalID, null, null, null); - return null; - } - - for (let [filter, callback] of results) { - if (filter.test(inputPath)) { - var result = callback({ - path: inputPath, - importer, - namespace: inputNamespace, - // resolveDir - kind, - // pluginData - }); - - while ( - result && - $isPromise(result) && - ($getPromiseInternalField(result, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - result = $getPromiseInternalField(result, $promiseFieldReactionsOrResult); - } - - if (result && $isPromise(result)) { - result = await result; - } - - if (!result || !$isObject(result)) { - continue; - } - - var { path, namespace: userNamespace = inputNamespace, external } = result; - if (!(typeof path === "string") || !(typeof userNamespace === "string")) { - throw new TypeError("onResolve plugins must return an object with a string 'path' and string 'loader' field"); - } - - if (!path) { - continue; - } - - if (!userNamespace) { - userNamespace = inputNamespace; - } - if (typeof external !== "boolean" && !$isUndefinedOrNull(external)) { - throw new TypeError('onResolve plugins "external" field must be boolean or unspecified'); - } - - if (!external) { - if (userNamespace === "file") { - if (process.platform !== "win32") { - if (path[0] !== "/" || path.includes("..")) { - throw new TypeError('onResolve plugin "path" must be absolute when the namespace is "file"'); - } - } else { - // TODO: Windows - } - } - if (userNamespace === "dataurl") { - if (!path.startsWith("data:")) { - throw new TypeError('onResolve plugin "path" must start with "data:" when the namespace is "dataurl"'); - } - } - - if (userNamespace && userNamespace !== "file" && (!onLoad || !onLoad.$has(userNamespace))) { - throw new TypeError(`Expected onLoad plugin for namespace ${userNamespace} to exist`); - } - } - this.onResolveAsync(internalID, path, userNamespace, external); - return null; - } - } - - this.onResolveAsync(internalID, null, null, null); - return null; - })(specifier, inputNamespace, importer, kind); - - while ( - promiseResult && - $isPromise(promiseResult) && - ($getPromiseInternalField(promiseResult, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - promiseResult = $getPromiseInternalField(promiseResult, $promiseFieldReactionsOrResult); - } - - if (promiseResult && $isPromise(promiseResult)) { - promiseResult.then( - () => {}, - e => { - this.addError(internalID, e, 0); - }, - ); - } -} - -export function runOnLoadPlugins(this: BundlerPlugin, internalID, path, namespace, defaultLoaderId) { - const LOADERS_MAP = $LoaderLabelToId; - const loaderName = $LoaderIdToLabel[defaultLoaderId]; - - var promiseResult = (async (internalID, path, namespace, defaultLoader) => { - var results = this.onLoad.$get(namespace); - if (!results) { - this.onLoadAsync(internalID, null, null); - return null; - } - - for (let [filter, callback] of results) { - if (filter.test(path)) { - var result = callback({ - path, - namespace, - // suffix - // pluginData - loader: defaultLoader, - }); - - while ( - result && - $isPromise(result) && - ($getPromiseInternalField(result, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - result = $getPromiseInternalField(result, $promiseFieldReactionsOrResult); - } - - if (result && $isPromise(result)) { - result = await result; - } - - if (!result || !$isObject(result)) { - continue; - } - - var { contents, loader = defaultLoader } = result as OnLoadResultSourceCode & OnLoadResultObject; - if (!(typeof contents === "string") && !$isTypedArrayView(contents)) { - throw new TypeError('onLoad plugins must return an object with "contents" as a string or Uint8Array'); - } - - if (!(typeof loader === "string")) { - throw new TypeError('onLoad plugins must return an object with "loader" as a string'); - } - - const chosenLoader = LOADERS_MAP[loader]; - if (chosenLoader === undefined) { - throw new TypeError(`Loader ${loader} is not supported.`); - } - - this.onLoadAsync(internalID, contents, chosenLoader); - return null; - } - } - - this.onLoadAsync(internalID, null, null); - return null; - })(internalID, path, namespace, loaderName); - - while ( - promiseResult && - $isPromise(promiseResult) && - ($getPromiseInternalField(promiseResult, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - promiseResult = $getPromiseInternalField(promiseResult, $promiseFieldReactionsOrResult); - } - - if (promiseResult && $isPromise(promiseResult)) { - promiseResult.then( - () => {}, - e => { - this.addError(internalID, e, 1); - }, - ); - } -} diff --git a/src/bun.js/builtins/ts/ByteLengthQueuingStrategy.ts b/src/bun.js/builtins/ts/ByteLengthQueuingStrategy.ts deleted file mode 100644 index fc3f3d998..000000000 --- a/src/bun.js/builtins/ts/ByteLengthQueuingStrategy.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. - * Copyright (C) 2015 Igalia S.L. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -$getter; -export function highWaterMark(this: any) { - const highWaterMark = $getByIdDirectPrivate(this, "highWaterMark"); - if (highWaterMark === undefined) - throw new TypeError("ByteLengthQueuingStrategy.highWaterMark getter called on incompatible |this| value."); - - return highWaterMark; -} - -export function size(chunk) { - return chunk.byteLength; -} - -export function initializeByteLengthQueuingStrategy(this: any, parameters: any) { - $putByIdDirectPrivate(this, "highWaterMark", $extractHighWaterMarkFromQueuingStrategyInit(parameters)); -} diff --git a/src/bun.js/builtins/ts/ConsoleObject.ts b/src/bun.js/builtins/ts/ConsoleObject.ts deleted file mode 100644 index 45746459a..000000000 --- a/src/bun.js/builtins/ts/ConsoleObject.ts +++ /dev/null @@ -1,84 +0,0 @@ -$overriddenName = "[Symbol.asyncIterator]"; -export function asyncIterator(this: Console) { - const Iterator = async function* ConsoleAsyncIterator() { - const stream = Bun.stdin.stream(); - var reader = stream.getReader(); - - // TODO: use builtin - var decoder = new (globalThis as any).TextDecoder("utf-8", { fatal: false }) as TextDecoder; - var deferredError; - var indexOf = Bun.indexOfLine; - - try { - while (true) { - var done, value; - var pendingChunk; - const firstResult = reader.readMany(); - if ($isPromise(firstResult)) { - ({ done, value } = await firstResult); - } else { - ({ done, value } = firstResult); - } - - if (done) { - if (pendingChunk) { - yield decoder.decode(pendingChunk); - } - return; - } - - var actualChunk; - // we assume it was given line-by-line - for (const chunk of value) { - actualChunk = chunk; - if (pendingChunk) { - actualChunk = Buffer.concat([pendingChunk, chunk]); - pendingChunk = null; - } - - var last = 0; - // TODO: "\r", 0x4048, 0x4049, 0x404A, 0x404B, 0x404C, 0x404D, 0x404E, 0x404F - var i = indexOf(actualChunk, last); - while (i !== -1) { - yield decoder.decode(actualChunk.subarray(last, i)); - last = i + 1; - i = indexOf(actualChunk, last); - } - - pendingChunk = actualChunk.subarray(last); - } - } - } catch (e) { - deferredError = e; - } finally { - reader.releaseLock(); - - if (deferredError) { - throw deferredError; - } - } - }; - - const symbol = globalThis.Symbol.asyncIterator; - this[symbol] = Iterator; - return Iterator(); -} - -export function write(this: Console, input) { - var writer = $getByIdDirectPrivate(this, "writer"); - if (!writer) { - var length = $toLength(input?.length ?? 0); - writer = Bun.stdout.writer({ highWaterMark: length > 65536 ? length : 65536 }); - $putByIdDirectPrivate(this, "writer", writer); - } - - var wrote = writer.write(input); - - const count = $argumentCount(); - for (var i = 1; i < count; i++) { - wrote += writer.write($argument(i)); - } - - writer.flush(true); - return wrote; -} diff --git a/src/bun.js/builtins/ts/CountQueuingStrategy.ts b/src/bun.js/builtins/ts/CountQueuingStrategy.ts deleted file mode 100644 index a72dca1ca..000000000 --- a/src/bun.js/builtins/ts/CountQueuingStrategy.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -$getter; -export function highWaterMark(this: any) { - const highWaterMark = $getByIdDirectPrivate(this, "highWaterMark"); - - if (highWaterMark === undefined) - throw new TypeError("CountQueuingStrategy.highWaterMark getter called on incompatible |this| value."); - - return highWaterMark; -} - -export function size() { - return 1; -} - -export function initializeCountQueuingStrategy(this: any, parameters: any) { - $putByIdDirectPrivate(this, "highWaterMark", $extractHighWaterMarkFromQueuingStrategyInit(parameters)); -} diff --git a/src/bun.js/builtins/ts/ImportMetaObject.ts b/src/bun.js/builtins/ts/ImportMetaObject.ts deleted file mode 100644 index 2df0f0c98..000000000 --- a/src/bun.js/builtins/ts/ImportMetaObject.ts +++ /dev/null @@ -1,257 +0,0 @@ -type ImportMetaObject = Partial<ImportMeta>; - -export function loadCJS2ESM(this: ImportMetaObject, resolvedSpecifier: string) { - var loader = Loader; - var queue = $createFIFO(); - var key = resolvedSpecifier; - while (key) { - // we need to explicitly check because state could be $ModuleFetch - // it will throw this error if we do not: - // $throwTypeError("Requested module is already fetched."); - var entry = loader.registry.$get(key); - - if (!entry || !entry.state || entry.state <= $ModuleFetch) { - $fulfillModuleSync(key); - entry = loader.registry.$get(key)!; - } - - // entry.fetch is a Promise<SourceCode> - // SourceCode is not a string, it's a JSC::SourceCode object - // this pulls it out of the promise without delaying by a tick - // the promise is already fullfilled by $fullfillModuleSync - var sourceCodeObject = $getPromiseInternalField(entry.fetch, $promiseFieldReactionsOrResult); - // parseModule() returns a Promise, but the value is already fulfilled - // so we just pull it out of the promise here once again - // But, this time we do it a little more carefully because this is a JSC function call and not bun source code - var moduleRecordPromise = loader.parseModule(key, sourceCodeObject); - var module = entry.module; - if (!module && moduleRecordPromise && $isPromise(moduleRecordPromise)) { - var reactionsOrResult = $getPromiseInternalField(moduleRecordPromise, $promiseFieldReactionsOrResult); - var flags = $getPromiseInternalField(moduleRecordPromise, $promiseFieldFlags); - var state = flags & $promiseStateMask; - // this branch should never happen, but just to be safe - if (state === $promiseStatePending || (reactionsOrResult && $isPromise(reactionsOrResult))) { - throw new TypeError(`require() async module "${key}" is unsupported`); - } else if (state === $promiseStateRejected) { - // TODO: use SyntaxError but preserve the specifier - throw new TypeError(`${reactionsOrResult?.message ?? "An error occurred"} while parsing module \"${key}\"`); - } - entry.module = module = reactionsOrResult; - } else if (moduleRecordPromise && !module) { - entry.module = module = moduleRecordPromise as LoaderModule; - } - - // This is very similar to "requestInstantiate" in ModuleLoader.js in JavaScriptCore. - $setStateToMax(entry, $ModuleLink); - var dependenciesMap = module.dependenciesMap; - var requestedModules = loader.requestedModules(module); - var dependencies = $newArrayWithSize<string>(requestedModules.length); - for (var i = 0, length = requestedModules.length; i < length; ++i) { - var depName = requestedModules[i]; - // optimization: if it starts with a slash then it's an absolute path - // we don't need to run the resolver a 2nd time - var depKey = depName[0] === "/" ? depName : loader.resolve(depName, key); - var depEntry = loader.ensureRegistered(depKey); - if (depEntry.state < $ModuleLink) { - queue.push(depKey); - } - - $putByValDirect(dependencies, i, depEntry); - dependenciesMap.$set(depName, depEntry); - } - - entry.dependencies = dependencies; - // All dependencies resolved, set instantiate and satisfy field directly. - entry.instantiate = Promise.resolve(entry); - entry.satisfy = Promise.resolve(entry); - key = queue.shift(); - while (key && (loader.registry.$get(key)?.state ?? $ModuleFetch) >= $ModuleLink) { - key = queue.shift(); - } - } - - var linkAndEvaluateResult = loader.linkAndEvaluateModule(resolvedSpecifier, undefined); - if (linkAndEvaluateResult && $isPromise(linkAndEvaluateResult)) { - // if you use top-level await, or any dependencies use top-level await, then we throw here - // this means the module will still actually load eventually, but that's okay. - throw new TypeError(`require() async module \"${resolvedSpecifier}\" is unsupported`); - } - - return loader.registry.$get(resolvedSpecifier); -} - -export function requireESM(this: ImportMetaObject, resolved) { - var entry = Loader.registry.$get(resolved); - - if (!entry || !entry.evaluated) { - entry = $loadCJS2ESM(resolved); - } - - if (!entry || !entry.evaluated || !entry.module) { - throw new TypeError(`require() failed to evaluate module "${resolved}". This is an internal consistentency error.`); - } - var exports = Loader.getModuleNamespaceObject(entry.module); - if (exports[$commonJSSymbol] === 0) { - // CommonJS module created via `Bun::CommonJSModuleRecord` - // We will refer to the requireMap to get the exports - return; - } - - var commonJS = exports.default; - var cjs = commonJS?.[$commonJSSymbol]; - if (cjs === 0) { - return commonJS; - } else if (cjs && $isCallable(commonJS)) { - return commonJS(); - } - - return exports; -} - -export function internalRequire(this: ImportMetaObject, resolved) { - var cached = $requireMap.$get(resolved); - const last5 = resolved.substring(resolved.length - 5); - if (cached) { - if (last5 === ".node") { - return cached.exports; - } - return cached; - } - - // TODO: remove this hardcoding - if (last5 === ".json") { - var fs = (globalThis[Symbol.for("_fs")] ||= Bun.fs()); - var exports = JSON.parse(fs.readFileSync(resolved, "utf8")); - $requireMap.$set(resolved, exports); - return exports; - } else if (last5 === ".node") { - var module = { exports: {} }; - process.dlopen(module, resolved); - $requireMap.$set(resolved, module); - return module.exports; - } else if (last5 === ".toml") { - var fs = (globalThis[Symbol.for("_fs")] ||= Bun.fs()); - var exports = Bun.TOML.parse(fs.readFileSync(resolved, "utf8")); - $requireMap.$set(resolved, exports); - return exports; - } else { - var exports = $requireESM(resolved); - const cachedExports = $requireMap.$get(resolved); - if (cachedExports) { - return cachedExports; - } - - $requireMap.$set(resolved, exports); - return exports; - } -} - -export function createRequireCache() { - class Module { - id; - parent; - filename; - children = []; - paths = []; - - constructor(filename) { - this.id = filename; - // TODO: windows - const lastSlash = filename.lastIndexOf("/"); - if (lastSlash !== -1 && filename.length > lastSlash + 1) { - this.filename = filename.substring(lastSlash + 1); - } else { - this.filename = filename; - } - } - - get loaded() { - return true; - } - - require(path) { - return $internalRequire($resolveSync(path, this.id)); - } - - get exports() { - return $requireMap.$get(this.id) ?? {}; - } - - set exports(value) { - $requireMap.$set(this.id, value); - } - } - - var moduleMap = new Map(); - - return new Proxy( - {}, - { - get(target, key: string) { - const entry = $requireMap.$get(key); - if (entry) { - var mod = moduleMap.$get(key); - if (!mod) { - mod = new Module(key); - moduleMap.$set(key, mod); - } - return mod; - } - }, - set(target, key: string, value) { - if (!moduleMap.$has(key)) { - moduleMap.$set(key, new Module(key)); - } - - $requireMap.$set(key, value?.exports); - - return true; - }, - - has(target, key: string) { - return $requireMap.$has(key); - }, - - deleteProperty(target, key: string) { - moduleMap.$delete(key); - $requireMap.$delete(key); - Loader.registry.$delete(key); - return true; - }, - - ownKeys(target) { - return [...$requireMap.$keys()]; - }, - - // In Node, require.cache has a null prototype - getPrototypeOf(target) { - return null; - }, - - getOwnPropertyDescriptor(target, key: string) { - if ($requireMap.$has(key)) { - return { - configurable: true, - enumerable: true, - }; - } - }, - }, - ); -} - -$sloppy; -export function require(this: ImportMetaObject, name) { - var from = this?.path ?? arguments.callee.path; - - if (typeof name !== "string") { - throw new TypeError("require(name) must be a string"); - } - - return $internalRequire($resolveSync(name, from)); -} - -$getter; -export function main(this: ImportMetaObject) { - return this.path === Bun.main; -} diff --git a/src/bun.js/builtins/ts/JSBufferConstructor.ts b/src/bun.js/builtins/ts/JSBufferConstructor.ts deleted file mode 100644 index debc62d51..000000000 --- a/src/bun.js/builtins/ts/JSBufferConstructor.ts +++ /dev/null @@ -1,67 +0,0 @@ -export function from(items) { - if ($isUndefinedOrNull(items)) { - throw new TypeError( - "The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object.", - ); - } - - // TODO: figure out why private symbol not found - if ( - typeof items === "string" || - (typeof items === "object" && - ($isTypedArrayView(items) || - items instanceof ArrayBuffer || - items instanceof SharedArrayBuffer || - items instanceof String)) - ) { - switch ($argumentCount()) { - case 1: { - return new $Buffer(items); - } - case 2: { - return new $Buffer(items, $argument(1)); - } - default: { - return new $Buffer(items, $argument(1), $argument(2)); - } - } - } - - var arrayLike = $toObject( - items, - "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.", - ) as ArrayLike<any>; - - if (!$isJSArray(arrayLike)) { - const toPrimitive = $tryGetByIdWithWellKnownSymbol(items, "toPrimitive"); - - if (toPrimitive) { - const primitive = toPrimitive.$call(items, "string"); - - if (typeof primitive === "string") { - switch ($argumentCount()) { - case 1: { - return new $Buffer(primitive); - } - case 2: { - return new $Buffer(primitive, $argument(1)); - } - default: { - return new $Buffer(primitive, $argument(1), $argument(2)); - } - } - } - } - - if (!("length" in arrayLike) || $isCallable(arrayLike)) { - throw new TypeError( - "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.", - ); - } - } - - // Don't pass the second argument because Node's Buffer.from doesn't accept - // a function and Uint8Array.from requires it if it exists - // That means we cannot use $tailCallFowrardArguments here, sadly - return new $Buffer(Uint8Array.from(arrayLike).buffer); -} diff --git a/src/bun.js/builtins/ts/JSBufferPrototype.ts b/src/bun.js/builtins/ts/JSBufferPrototype.ts deleted file mode 100644 index 97b25b9b2..000000000 --- a/src/bun.js/builtins/ts/JSBufferPrototype.ts +++ /dev/null @@ -1,495 +0,0 @@ -// The fastest way as of April 2022 is to use DataView. -// DataView has intrinsics that cause inlining - -interface BufferExt extends Buffer { - $dataView?: DataView; - - toString(encoding?: BufferEncoding, start?: number, end?: number): string; - toString(offset: number, length: number, encoding?: BufferEncoding): string; -} - -export function setBigUint64(this: BufferExt, offset, value, le) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigUint64( - offset, - value, - le, - ); -} -export function readInt8(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt8(offset); -} -export function readUInt8(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint8(offset); -} -export function readInt16LE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt16(offset, true); -} -export function readInt16BE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt16(offset, false); -} -export function readUInt16LE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint16(offset, true); -} -export function readUInt16BE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint16(offset, false); -} -export function readInt32LE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt32(offset, true); -} -export function readInt32BE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt32(offset, false); -} -export function readUInt32LE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint32(offset, true); -} -export function readUInt32BE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint32(offset, false); -} - -export function readIntLE(this: BufferExt, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - return view.getInt8(offset); - } - case 2: { - return view.getInt16(offset, true); - } - case 3: { - const val = view.getUint16(offset, true) + view.getUint8(offset + 2) * 2 ** 16; - return val | ((val & (2 ** 23)) * 0x1fe); - } - case 4: { - return view.getInt32(offset, true); - } - case 5: { - const last = view.getUint8(offset + 4); - return (last | ((last & (2 ** 7)) * 0x1fffffe)) * 2 ** 32 + view.getUint32(offset, true); - } - case 6: { - const last = view.getUint16(offset + 4, true); - return (last | ((last & (2 ** 15)) * 0x1fffe)) * 2 ** 32 + view.getUint32(offset, true); - } - } - throw new RangeError("byteLength must be >= 1 and <= 6"); -} -export function readIntBE(this: BufferExt, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - return view.getInt8(offset); - } - case 2: { - return view.getInt16(offset, false); - } - case 3: { - const val = view.getUint16(offset + 1, false) + view.getUint8(offset) * 2 ** 16; - return val | ((val & (2 ** 23)) * 0x1fe); - } - case 4: { - return view.getInt32(offset, false); - } - case 5: { - const last = view.getUint8(offset); - return (last | ((last & (2 ** 7)) * 0x1fffffe)) * 2 ** 32 + view.getUint32(offset + 1, false); - } - case 6: { - const last = view.getUint16(offset, false); - return (last | ((last & (2 ** 15)) * 0x1fffe)) * 2 ** 32 + view.getUint32(offset + 2, false); - } - } - throw new RangeError("byteLength must be >= 1 and <= 6"); -} -export function readUIntLE(this: BufferExt, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - return view.getUint8(offset); - } - case 2: { - return view.getUint16(offset, true); - } - case 3: { - return view.getUint16(offset, true) + view.getUint8(offset + 2) * 2 ** 16; - } - case 4: { - return view.getUint32(offset, true); - } - case 5: { - return view.getUint8(offset + 4) * 2 ** 32 + view.getUint32(offset, true); - } - case 6: { - return view.getUint16(offset + 4, true) * 2 ** 32 + view.getUint32(offset, true); - } - } - throw new RangeError("byteLength must be >= 1 and <= 6"); -} -export function readUIntBE(this: BufferExt, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - return view.getUint8(offset); - } - case 2: { - return view.getUint16(offset, false); - } - case 3: { - return view.getUint16(offset + 1, false) + view.getUint8(offset) * 2 ** 16; - } - case 4: { - return view.getUint32(offset, false); - } - case 5: { - const last = view.getUint8(offset); - return (last | ((last & (2 ** 7)) * 0x1fffffe)) * 2 ** 32 + view.getUint32(offset + 1, false); - } - case 6: { - const last = view.getUint16(offset, false); - return (last | ((last & (2 ** 15)) * 0x1fffe)) * 2 ** 32 + view.getUint32(offset + 2, false); - } - } - throw new RangeError("byteLength must be >= 1 and <= 6"); -} - -export function readFloatLE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat32(offset, true); -} -export function readFloatBE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat32(offset, false); -} -export function readDoubleLE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat64(offset, true); -} -export function readDoubleBE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat64(offset, false); -} -export function readBigInt64LE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigInt64(offset, true); -} -export function readBigInt64BE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigInt64(offset, false); -} -export function readBigUInt64LE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigUint64(offset, true); -} -export function readBigUInt64BE(this: BufferExt, offset) { - return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigUint64(offset, false); -} - -export function writeInt8(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt8(offset, value); - return offset + 1; -} -export function writeUInt8(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint8(offset, value); - return offset + 1; -} -export function writeInt16LE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt16(offset, value, true); - return offset + 2; -} -export function writeInt16BE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt16(offset, value, false); - return offset + 2; -} -export function writeUInt16LE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint16(offset, value, true); - return offset + 2; -} -export function writeUInt16BE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint16(offset, value, false); - return offset + 2; -} -export function writeInt32LE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt32(offset, value, true); - return offset + 4; -} -export function writeInt32BE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt32(offset, value, false); - return offset + 4; -} -export function writeUInt32LE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint32(offset, value, true); - return offset + 4; -} -export function writeUInt32BE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint32(offset, value, false); - return offset + 4; -} - -export function writeIntLE(this: BufferExt, value, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - view.setInt8(offset, value); - break; - } - case 2: { - view.setInt16(offset, value, true); - break; - } - case 3: { - view.setUint16(offset, value & 0xffff, true); - view.setInt8(offset + 2, Math.floor(value * 2 ** -16)); - break; - } - case 4: { - view.setInt32(offset, value, true); - break; - } - case 5: { - view.setUint32(offset, value | 0, true); - view.setInt8(offset + 4, Math.floor(value * 2 ** -32)); - break; - } - case 6: { - view.setUint32(offset, value | 0, true); - view.setInt16(offset + 4, Math.floor(value * 2 ** -32), true); - break; - } - default: { - throw new RangeError("byteLength must be >= 1 and <= 6"); - } - } - return offset + byteLength; -} -export function writeIntBE(this: BufferExt, value, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - view.setInt8(offset, value); - break; - } - case 2: { - view.setInt16(offset, value, false); - break; - } - case 3: { - view.setUint16(offset + 1, value & 0xffff, false); - view.setInt8(offset, Math.floor(value * 2 ** -16)); - break; - } - case 4: { - view.setInt32(offset, value, false); - break; - } - case 5: { - view.setUint32(offset + 1, value | 0, false); - view.setInt8(offset, Math.floor(value * 2 ** -32)); - break; - } - case 6: { - view.setUint32(offset + 2, value | 0, false); - view.setInt16(offset, Math.floor(value * 2 ** -32), false); - break; - } - default: { - throw new RangeError("byteLength must be >= 1 and <= 6"); - } - } - return offset + byteLength; -} -export function writeUIntLE(this: BufferExt, value, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - view.setUint8(offset, value); - break; - } - case 2: { - view.setUint16(offset, value, true); - break; - } - case 3: { - view.setUint16(offset, value & 0xffff, true); - view.setUint8(offset + 2, Math.floor(value * 2 ** -16)); - break; - } - case 4: { - view.setUint32(offset, value, true); - break; - } - case 5: { - view.setUint32(offset, value | 0, true); - view.setUint8(offset + 4, Math.floor(value * 2 ** -32)); - break; - } - case 6: { - view.setUint32(offset, value | 0, true); - view.setUint16(offset + 4, Math.floor(value * 2 ** -32), true); - break; - } - default: { - throw new RangeError("byteLength must be >= 1 and <= 6"); - } - } - return offset + byteLength; -} -export function writeUIntBE(this: BufferExt, value, offset, byteLength) { - const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); - switch (byteLength) { - case 1: { - view.setUint8(offset, value); - break; - } - case 2: { - view.setUint16(offset, value, false); - break; - } - case 3: { - view.setUint16(offset + 1, value & 0xffff, false); - view.setUint8(offset, Math.floor(value * 2 ** -16)); - break; - } - case 4: { - view.setUint32(offset, value, false); - break; - } - case 5: { - view.setUint32(offset + 1, value | 0, false); - view.setUint8(offset, Math.floor(value * 2 ** -32)); - break; - } - case 6: { - view.setUint32(offset + 2, value | 0, false); - view.setUint16(offset, Math.floor(value * 2 ** -32), false); - break; - } - default: { - throw new RangeError("byteLength must be >= 1 and <= 6"); - } - } - return offset + byteLength; -} - -export function writeFloatLE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat32(offset, value, true); - return offset + 4; -} - -export function writeFloatBE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat32(offset, value, false); - return offset + 4; -} - -export function writeDoubleLE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat64(offset, value, true); - return offset + 8; -} - -export function writeDoubleBE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat64(offset, value, false); - return offset + 8; -} - -export function writeBigInt64LE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigInt64(offset, value, true); - return offset + 8; -} - -export function writeBigInt64BE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigInt64(offset, value, false); - return offset + 8; -} - -export function writeBigUInt64LE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigUint64(offset, value, true); - return offset + 8; -} - -export function writeBigUInt64BE(this: BufferExt, value, offset) { - (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigUint64(offset, value, false); - return offset + 8; -} - -export function utf8Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "utf8"); -} -export function ucs2Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "ucs2"); -} -export function utf16leWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "utf16le"); -} -export function latin1Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "latin1"); -} -export function asciiWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "ascii"); -} -export function base64Write(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "base64"); -} -export function base64urlWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "base64url"); -} -export function hexWrite(this: BufferExt, text, offset, length) { - return this.write(text, offset, length, "hex"); -} - -export function utf8Slice(this: BufferExt, offset, length) { - return this.toString(offset, length, "utf8"); -} -export function ucs2Slice(this: BufferExt, offset, length) { - return this.toString(offset, length, "ucs2"); -} -export function utf16leSlice(this: BufferExt, offset, length) { - return this.toString(offset, length, "utf16le"); -} -export function latin1Slice(this: BufferExt, offset, length) { - return this.toString(offset, length, "latin1"); -} -export function asciiSlice(this: BufferExt, offset, length) { - return this.toString(offset, length, "ascii"); -} -export function base64Slice(this: BufferExt, offset, length) { - return this.toString(offset, length, "base64"); -} -export function base64urlSlice(this: BufferExt, offset, length) { - return this.toString(offset, length, "base64url"); -} -export function hexSlice(this: BufferExt, offset, length) { - return this.toString(offset, length, "hex"); -} - -export function toJSON(this: BufferExt) { - const type = "Buffer"; - const data = Array.from(this); - return { type, data }; -} - -export function slice(this: BufferExt, start, end) { - var { buffer, byteOffset, byteLength } = this; - - function adjustOffset(offset, length) { - // Use Math.trunc() to convert offset to an integer value that can be larger - // than an Int32. Hence, don't use offset | 0 or similar techniques. - offset = $trunc(offset); - if (offset === 0 || isNaN(offset)) { - return 0; - } else if (offset < 0) { - offset += length; - return offset > 0 ? offset : 0; - } else { - return offset < length ? offset : length; - } - } - - var start_ = adjustOffset(start, byteLength); - var end_ = end !== undefined ? adjustOffset(end, byteLength) : byteLength; - return new $Buffer(buffer, byteOffset + start_, end_ > start_ ? end_ - start_ : 0); -} - -$getter; -export function parent(this: BufferExt) { - return $isObject(this) && this instanceof $Buffer ? this.buffer : undefined; -} - -$getter; -export function offset(this: BufferExt) { - return $isObject(this) && this instanceof $Buffer ? this.byteOffset : undefined; -} - -export function inspect(this: BufferExt, recurseTimes, ctx) { - return Bun.inspect(this); -} diff --git a/src/bun.js/builtins/ts/ProcessObjectInternals.ts b/src/bun.js/builtins/ts/ProcessObjectInternals.ts deleted file mode 100644 index 8b24e68ba..000000000 --- a/src/bun.js/builtins/ts/ProcessObjectInternals.ts +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Copyright 2023 Codeblog Corp. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function binding(bindingName) { - if (bindingName !== "constants") - throw new TypeError( - "process.binding() is not supported in Bun. If that breaks something, please file an issue and include a reproducible code sample.", - ); - - var cache = globalThis.Symbol.for("process.bindings.constants"); - var constants = globalThis[cache]; - if (!constants) { - // TODO: make this less hacky. - // This calls require("node:fs").constants - // except, outside an ESM module. - const { constants: fs } = globalThis[globalThis.Symbol.for("Bun.lazy")]("createImportMeta", "node:process").require( - "node:fs", - ); - constants = { - fs, - zlib: {}, - crypto: {}, - os: Bun._Os().constants, - }; - globalThis[cache] = constants; - } - return constants; -} - -export function getStdioWriteStream(fd_, rawRequire) { - var module = { path: "node:process", require: rawRequire }; - var require = path => module.require(path); - - function createStdioWriteStream(fd_) { - var { Duplex, eos, destroy } = require("node:stream"); - var StdioWriteStream = class StdioWriteStream extends Duplex { - #writeStream; - #readStream; - - #readable = true; - #writable = true; - #fdPath; - - #onClose; - #onDrain; - #onFinish; - #onReadable; - #isTTY; - - get isTTY() { - return (this.#isTTY ??= require("node:tty").isatty(fd_)); - } - - get fd() { - return fd_; - } - - constructor(fd) { - super({ readable: true, writable: true }); - this.#fdPath = `/dev/fd/${fd}`; - } - - #onFinished(err) { - const cb = this.#onClose; - this.#onClose = null; - - if (cb) { - cb(err); - } else if (err) { - this.destroy(err); - } else if (!this.#readable && !this.#writable) { - this.destroy(); - } - } - - _destroy(err, callback) { - if (!err && this.#onClose !== null) { - var AbortError = class AbortError extends Error { - code: string; - name: string; - constructor(message = "The operation was aborted", options = void 0) { - if (options !== void 0 && typeof options !== "object") { - throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`); - } - super(message, options); - this.code = "ABORT_ERR"; - this.name = "AbortError"; - } - }; - err = new AbortError(); - } - - this.#onDrain = null; - this.#onFinish = null; - if (this.#onClose === null) { - callback(err); - } else { - this.#onClose = callback; - if (this.#writeStream) destroy(this.#writeStream, err); - if (this.#readStream) destroy(this.#readStream, err); - } - } - - _write(chunk, encoding, callback) { - if (!this.#writeStream) { - var { createWriteStream } = require("node:fs"); - var stream = (this.#writeStream = createWriteStream(this.#fdPath)); - - stream.on("finish", () => { - if (this.#onFinish) { - const cb = this.#onFinish; - this.#onFinish = null; - cb(); - } - }); - - stream.on("drain", () => { - if (this.#onDrain) { - const cb = this.#onDrain; - this.#onDrain = null; - cb(); - } - }); - - eos(stream, err => { - this.#writable = false; - if (err) { - destroy(stream, err); - } - this.#onFinished(err); - }); - } - if (stream.write(chunk, encoding)) { - callback(); - } else { - this.#onDrain = callback; - } - } - - _final(callback) { - this.#writeStream && this.#writeStream.end(); - this.#onFinish = callback; - } - - #loadReadStream() { - var { createReadStream } = require("node:fs"); - - var readStream = (this.#readStream = createReadStream(this.#fdPath)); - - readStream.on("readable", () => { - if (this.#onReadable) { - const cb = this.#onReadable; - this.#onReadable = null; - cb(); - } else { - this.read(); - } - }); - - readStream.on("end", () => { - this.push(null); - }); - - eos(readStream, err => { - this.#readable = false; - if (err) { - destroy(readStream, err); - } - this.#onFinished(err); - }); - return readStream; - } - - _read() { - var stream = this.#readStream; - if (!stream) { - stream = this.#loadReadStream(); - } - - while (true) { - const buf = stream.read(); - if (buf === null || !this.push(buf)) { - return; - } - } - } - }; - return new StdioWriteStream(fd_); - } - - var { EventEmitter } = require("node:events"); - - function isFastEncoding(encoding) { - if (!encoding) return true; - - var normalied = encoding.toLowerCase(); - return normalied === "utf8" || normalied === "utf-8" || normalied === "buffer" || normalied === "binary"; - } - - var readline; - - var FastStdioWriteStream = class StdioWriteStream extends EventEmitter { - #fd; - #innerStream; - #writer; - #isTTY; - - bytesWritten = 0; - - setDefaultEncoding(encoding) { - if (this.#innerStream || !isFastEncoding(encoding)) { - this.#ensureInnerStream(); - return this.#innerStream.setDefaultEncoding(encoding); - } - } - - #createWriter() { - switch (this.#fd) { - case 1: { - var writer = Bun.stdout.writer({ highWaterMark: 0 }); - writer.unref(); - return writer; - } - - case 2: { - var writer = Bun.stderr.writer({ highWaterMark: 0 }); - writer.unref(); - return writer; - } - default: { - throw new Error("Unsupported writer"); - } - } - } - - #getWriter() { - return (this.#writer ??= this.#createWriter()); - } - - constructor(fd_) { - super(); - this.#fd = fd_; - } - - get fd() { - return this.#fd; - } - - get isTTY() { - return (this.#isTTY ??= require("node:tty").isatty(this.#fd)); - } - - cursorTo(x, y, callback) { - return (readline ??= require("readline")).cursorTo(this, x, y, callback); - } - - moveCursor(dx, dy, callback) { - return (readline ??= require("readline")).moveCursor(this, dx, dy, callback); - } - - clearLine(dir, callback) { - return (readline ??= require("readline")).clearLine(this, dir, callback); - } - - clearScreenDown(callback) { - return (readline ??= require("readline")).clearScreenDown(this, callback); - } - - // TODO: once implemented this.columns and this.rows should be uncommented - // getWindowSize() { - // return [this.columns, this.rows]; - // } - - ref() { - this.#getWriter().ref(); - } - - unref() { - this.#getWriter().unref(); - } - - on(event, listener) { - if (event === "close" || event === "finish") { - this.#ensureInnerStream(); - return this.#innerStream.on(event, listener); - } - - if (event === "drain") { - return super.on("drain", listener); - } - - if (event === "error") { - return super.on("error", listener); - } - - return super.on(event, listener); - } - - get _writableState() { - this.#ensureInnerStream(); - return this.#innerStream._writableState; - } - - get _readableState() { - this.#ensureInnerStream(); - return this.#innerStream._readableState; - } - - pipe(destination) { - this.#ensureInnerStream(); - return this.#innerStream.pipe(destination); - } - - unpipe(destination) { - this.#ensureInnerStream(); - return this.#innerStream.unpipe(destination); - } - - #ensureInnerStream() { - if (this.#innerStream) return; - this.#innerStream = createStdioWriteStream(this.#fd); - const events = this.eventNames(); - for (const event of events) { - this.#innerStream.on(event, (...args) => { - this.emit(event, ...args); - }); - } - } - - #write1(chunk) { - var writer = this.#getWriter(); - const writeResult = writer.write(chunk); - this.bytesWritten += writeResult; - const flushResult = writer.flush(false); - return !!(writeResult || flushResult); - } - - #writeWithEncoding(chunk, encoding) { - if (!isFastEncoding(encoding)) { - this.#ensureInnerStream(); - return this.#innerStream.write(chunk, encoding); - } - - return this.#write1(chunk); - } - - #performCallback(cb, err?: any) { - if (err) { - this.emit("error", err); - } - - try { - cb(err ? err : null); - } catch (err2) { - this.emit("error", err2); - } - } - - #writeWithCallbackAndEncoding(chunk, encoding, callback) { - if (!isFastEncoding(encoding)) { - this.#ensureInnerStream(); - return this.#innerStream.write(chunk, encoding, callback); - } - - var writer = this.#getWriter(); - const writeResult = writer.write(chunk); - const flushResult = writer.flush(true); - if (flushResult?.then) { - flushResult.then( - () => { - this.#performCallback(callback); - this.emit("drain"); - }, - err => this.#performCallback(callback, err), - ); - return false; - } - - queueMicrotask(() => { - this.#performCallback(callback); - }); - - return !!(writeResult || flushResult); - } - - write(chunk, encoding, callback) { - const result = this._write(chunk, encoding, callback); - - if (result) { - this.emit("drain"); - } - - return result; - } - - get hasColors() { - return Bun.tty[this.#fd].hasColors; - } - - _write(chunk, encoding, callback) { - var inner = this.#innerStream; - if (inner) { - return inner.write(chunk, encoding, callback); - } - - switch (arguments.length) { - case 0: { - var error = new Error("Invalid arguments"); - error.code = "ERR_INVALID_ARG_TYPE"; - throw error; - } - case 1: { - return this.#write1(chunk); - } - case 2: { - if (typeof encoding === "function") { - return this.#writeWithCallbackAndEncoding(chunk, "", encoding); - } else if (typeof encoding === "string") { - return this.#writeWithEncoding(chunk, encoding); - } - } - default: { - if ( - (typeof encoding !== "undefined" && typeof encoding !== "string") || - (typeof callback !== "undefined" && typeof callback !== "function") - ) { - var error = new Error("Invalid arguments"); - error.code = "ERR_INVALID_ARG_TYPE"; - throw error; - } - - if (typeof callback === "undefined") { - return this.#writeWithEncoding(chunk, encoding); - } - - return this.#writeWithCallbackAndEncoding(chunk, encoding, callback); - } - } - } - - destroy() { - return this; - } - - end() { - return this; - } - }; - - return new FastStdioWriteStream(fd_); -} - -export function getStdinStream(fd_, rawRequire, Bun) { - var module = { path: "node:process", require: rawRequire }; - var require = path => module.require(path); - - var { Duplex, eos, destroy } = require("node:stream"); - - var StdinStream = class StdinStream extends Duplex { - #reader; - // TODO: investigate https://github.com/oven-sh/bun/issues/1607 - - #readRef; - #writeStream; - - #readable = true; - #unrefOnRead = false; - #writable = true; - - #onFinish; - #onClose; - #onDrain; - - get isTTY() { - return require("tty").isatty(fd_); - } - - get fd() { - return fd_; - } - - constructor() { - super({ readable: true, writable: true }); - } - - #onFinished(err?) { - const cb = this.#onClose; - this.#onClose = null; - - if (cb) { - cb(err); - } else if (err) { - this.destroy(err); - } else if (!this.#readable && !this.#writable) { - this.destroy(); - } - } - - _destroy(err, callback) { - if (!err && this.#onClose !== null) { - var AbortError = class AbortError extends Error { - constructor(message = "The operation was aborted", options = void 0) { - if (options !== void 0 && typeof options !== "object") { - throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`); - } - super(message, options); - this.code = "ABORT_ERR"; - this.name = "AbortError"; - } - }; - err = new AbortError(); - } - - if (this.#onClose === null) { - callback(err); - } else { - this.#onClose = callback; - if (this.#writeStream) destroy(this.#writeStream, err); - } - } - - setRawMode(mode) {} - - on(name, callback) { - // Streams don't generally required to present any data when only - // `readable` events are present, i.e. `readableFlowing === false` - // - // However, Node.js has a this quirk whereby `process.stdin.read()` - // blocks under TTY mode, thus looping `.read()` in this particular - // case would not result in truncation. - // - // Therefore the following hack is only specific to `process.stdin` - // and does not apply to the underlying Stream implementation. - if (name === "readable") { - this.ref(); - this.#unrefOnRead = true; - } - return super.on(name, callback); - } - - pause() { - this.unref(); - return super.pause(); - } - - resume() { - this.ref(); - return super.resume(); - } - - ref() { - this.#reader ??= Bun.stdin.stream().getReader(); - this.#readRef ??= setInterval(() => {}, 1 << 30); - } - - unref() { - if (this.#readRef) { - clearInterval(this.#readRef); - this.#readRef = null; - } - } - - async #readInternal() { - try { - var done, value; - const read = this.#reader.readMany(); - - // read same-tick if possible - if (!read?.then) { - ({ done, value } = read); - } else { - ({ done, value } = await read); - } - - if (!done) { - this.push(value[0]); - - // shouldn't actually happen, but just in case - const length = value.length; - for (let i = 1; i < length; i++) { - this.push(value[i]); - } - } else { - this.push(null); - this.pause(); - this.#readable = false; - this.#onFinished(); - } - } catch (err) { - this.#readable = false; - this.#onFinished(err); - } - } - - _read(size) { - if (this.#unrefOnRead) { - this.unref(); - this.#unrefOnRead = false; - } - this.#readInternal(); - } - - #constructWriteStream() { - var { createWriteStream } = require("node:fs"); - var writeStream = (this.#writeStream = createWriteStream("/dev/fd/0")); - - writeStream.on("finish", () => { - if (this.#onFinish) { - const cb = this.#onFinish; - this.#onFinish = null; - cb(); - } - }); - - writeStream.on("drain", () => { - if (this.#onDrain) { - const cb = this.#onDrain; - this.#onDrain = null; - cb(); - } - }); - - eos(writeStream, err => { - this.#writable = false; - if (err) { - destroy(writeStream, err); - } - this.#onFinished(err); - }); - - return writeStream; - } - - _write(chunk, encoding, callback) { - var writeStream = this.#writeStream; - if (!writeStream) { - writeStream = this.#constructWriteStream(); - } - - if (writeStream.write(chunk, encoding)) { - callback(); - } else { - this.#onDrain = callback; - } - } - - _final(callback) { - this.#writeStream.end(); - this.#onFinish = (...args) => callback(...args); - } - }; - - return new StdinStream(); -} diff --git a/src/bun.js/builtins/ts/ReadableByteStreamController.ts b/src/bun.js/builtins/ts/ReadableByteStreamController.ts deleted file mode 100644 index 888f241bc..000000000 --- a/src/bun.js/builtins/ts/ReadableByteStreamController.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2016 Canon Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeReadableByteStreamController(this, stream, underlyingByteSource, highWaterMark) { - if (arguments.length !== 4 && arguments[3] !== $isReadableStream) - throw new TypeError("ReadableByteStreamController constructor should not be called directly"); - - return $privateInitializeReadableByteStreamController.$call(this, stream, underlyingByteSource, highWaterMark); -} - -export function enqueue(this, chunk) { - if (!$isReadableByteStreamController(this)) throw $makeThisTypeError("ReadableByteStreamController", "enqueue"); - - if ($getByIdDirectPrivate(this, "closeRequested")) - throw new TypeError("ReadableByteStreamController is requested to close"); - - if ($getByIdDirectPrivate($getByIdDirectPrivate(this, "controlledReadableStream"), "state") !== $streamReadable) - throw new TypeError("ReadableStream is not readable"); - - if (!$isObject(chunk) || !ArrayBuffer.$isView(chunk)) throw new TypeError("Provided chunk is not a TypedArray"); - - return $readableByteStreamControllerEnqueue(this, chunk); -} - -export function error(this, error) { - if (!$isReadableByteStreamController(this)) throw $makeThisTypeError("ReadableByteStreamController", "error"); - - if ($getByIdDirectPrivate($getByIdDirectPrivate(this, "controlledReadableStream"), "state") !== $streamReadable) - throw new TypeError("ReadableStream is not readable"); - - $readableByteStreamControllerError(this, error); -} - -export function close(this) { - if (!$isReadableByteStreamController(this)) throw $makeThisTypeError("ReadableByteStreamController", "close"); - - if ($getByIdDirectPrivate(this, "closeRequested")) throw new TypeError("Close has already been requested"); - - if ($getByIdDirectPrivate($getByIdDirectPrivate(this, "controlledReadableStream"), "state") !== $streamReadable) - throw new TypeError("ReadableStream is not readable"); - - $readableByteStreamControllerClose(this); -} - -$getter; -export function byobRequest(this) { - if (!$isReadableByteStreamController(this)) throw $makeGetterTypeError("ReadableByteStreamController", "byobRequest"); - - var request = $getByIdDirectPrivate(this, "byobRequest"); - if (request === undefined) { - var pending = $getByIdDirectPrivate(this, "pendingPullIntos"); - const firstDescriptor = pending.peek(); - if (firstDescriptor) { - const view = new Uint8Array( - firstDescriptor.buffer, - firstDescriptor.byteOffset + firstDescriptor.bytesFilled, - firstDescriptor.byteLength - firstDescriptor.bytesFilled, - ); - $putByIdDirectPrivate(this, "byobRequest", new ReadableStreamBYOBRequest(this, view, $isReadableStream)); - } - } - - return $getByIdDirectPrivate(this, "byobRequest"); -} - -$getter; -export function desiredSize(this) { - if (!$isReadableByteStreamController(this)) throw $makeGetterTypeError("ReadableByteStreamController", "desiredSize"); - - return $readableByteStreamControllerGetDesiredSize(this); -} diff --git a/src/bun.js/builtins/ts/ReadableByteStreamInternals.ts b/src/bun.js/builtins/ts/ReadableByteStreamInternals.ts deleted file mode 100644 index f44c385b4..000000000 --- a/src/bun.js/builtins/ts/ReadableByteStreamInternals.ts +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright (C) 2016 Canon Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -// @internal - -export function privateInitializeReadableByteStreamController(this, stream, underlyingByteSource, highWaterMark) { - if (!$isReadableStream(stream)) throw new TypeError("ReadableByteStreamController needs a ReadableStream"); - - // readableStreamController is initialized with null value. - if ($getByIdDirectPrivate(stream, "readableStreamController") !== null) - throw new TypeError("ReadableStream already has a controller"); - - $putByIdDirectPrivate(this, "controlledReadableStream", stream); - $putByIdDirectPrivate(this, "underlyingByteSource", underlyingByteSource); - $putByIdDirectPrivate(this, "pullAgain", false); - $putByIdDirectPrivate(this, "pulling", false); - $readableByteStreamControllerClearPendingPullIntos(this); - $putByIdDirectPrivate(this, "queue", $newQueue()); - $putByIdDirectPrivate(this, "started", 0); - $putByIdDirectPrivate(this, "closeRequested", false); - - let hwm = $toNumber(highWaterMark); - if (isNaN(hwm) || hwm < 0) throw new RangeError("highWaterMark value is negative or not a number"); - $putByIdDirectPrivate(this, "strategyHWM", hwm); - - let autoAllocateChunkSize = underlyingByteSource.autoAllocateChunkSize; - if (autoAllocateChunkSize !== undefined) { - autoAllocateChunkSize = $toNumber(autoAllocateChunkSize); - if (autoAllocateChunkSize <= 0 || autoAllocateChunkSize === Infinity || autoAllocateChunkSize === -Infinity) - throw new RangeError("autoAllocateChunkSize value is negative or equal to positive or negative infinity"); - } - $putByIdDirectPrivate(this, "autoAllocateChunkSize", autoAllocateChunkSize); - $putByIdDirectPrivate(this, "pendingPullIntos", $createFIFO()); - - const controller = this; - $promiseInvokeOrNoopNoCatch($getByIdDirectPrivate(controller, "underlyingByteSource"), "start", [controller]).$then( - () => { - $putByIdDirectPrivate(controller, "started", 1); - $assert(!$getByIdDirectPrivate(controller, "pulling")); - $assert(!$getByIdDirectPrivate(controller, "pullAgain")); - $readableByteStreamControllerCallPullIfNeeded(controller); - }, - error => { - if ($getByIdDirectPrivate(stream, "state") === $streamReadable) - $readableByteStreamControllerError(controller, error); - }, - ); - - $putByIdDirectPrivate(this, "cancel", $readableByteStreamControllerCancel); - $putByIdDirectPrivate(this, "pull", $readableByteStreamControllerPull); - - return this; -} - -export function readableStreamByteStreamControllerStart(this, controller) { - $putByIdDirectPrivate(controller, "start", undefined); -} - -export function privateInitializeReadableStreamBYOBRequest(this, controller, view) { - $putByIdDirectPrivate(this, "associatedReadableByteStreamController", controller); - $putByIdDirectPrivate(this, "view", view); -} - -export function isReadableByteStreamController(controller) { - // Same test mechanism as in isReadableStreamDefaultController (ReadableStreamInternals.js). - // See corresponding function for explanations. - return $isObject(controller) && !!$getByIdDirectPrivate(controller, "underlyingByteSource"); -} - -export function isReadableStreamBYOBRequest(byobRequest) { - // Same test mechanism as in isReadableStreamDefaultController (ReadableStreamInternals.js). - // See corresponding function for explanations. - return $isObject(byobRequest) && !!$getByIdDirectPrivate(byobRequest, "associatedReadableByteStreamController"); -} - -export function isReadableStreamBYOBReader(reader) { - // Spec tells to return true only if reader has a readIntoRequests internal slot. - // However, since it is a private slot, it cannot be checked using hasOwnProperty(). - // Since readIntoRequests is initialized with an empty array, the following test is ok. - return $isObject(reader) && !!$getByIdDirectPrivate(reader, "readIntoRequests"); -} - -export function readableByteStreamControllerCancel(controller, reason) { - var pendingPullIntos = $getByIdDirectPrivate(controller, "pendingPullIntos"); - var first = pendingPullIntos.peek(); - if (first) first.bytesFilled = 0; - - $putByIdDirectPrivate(controller, "queue", $newQueue()); - return $promiseInvokeOrNoop($getByIdDirectPrivate(controller, "underlyingByteSource"), "cancel", [reason]); -} - -export function readableByteStreamControllerError(controller, e) { - $assert( - $getByIdDirectPrivate($getByIdDirectPrivate(controller, "controlledReadableStream"), "state") === $streamReadable, - ); - $readableByteStreamControllerClearPendingPullIntos(controller); - $putByIdDirectPrivate(controller, "queue", $newQueue()); - $readableStreamError($getByIdDirectPrivate(controller, "controlledReadableStream"), e); -} - -export function readableByteStreamControllerClose(controller) { - $assert(!$getByIdDirectPrivate(controller, "closeRequested")); - $assert( - $getByIdDirectPrivate($getByIdDirectPrivate(controller, "controlledReadableStream"), "state") === $streamReadable, - ); - - if ($getByIdDirectPrivate(controller, "queue").size > 0) { - $putByIdDirectPrivate(controller, "closeRequested", true); - return; - } - - var first = $getByIdDirectPrivate(controller, "pendingPullIntos")?.peek(); - if (first) { - if (first.bytesFilled > 0) { - const e = $makeTypeError("Close requested while there remain pending bytes"); - $readableByteStreamControllerError(controller, e); - throw e; - } - } - - $readableStreamClose($getByIdDirectPrivate(controller, "controlledReadableStream")); -} - -export function readableByteStreamControllerClearPendingPullIntos(controller) { - $readableByteStreamControllerInvalidateBYOBRequest(controller); - var existing = $getByIdDirectPrivate(controller, "pendingPullIntos"); - if (existing !== undefined) { - existing.clear(); - } else { - $putByIdDirectPrivate(controller, "pendingPullIntos", $createFIFO()); - } -} - -export function readableByteStreamControllerGetDesiredSize(controller) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - const state = $getByIdDirectPrivate(stream, "state"); - - if (state === $streamErrored) return null; - if (state === $streamClosed) return 0; - - return $getByIdDirectPrivate(controller, "strategyHWM") - $getByIdDirectPrivate(controller, "queue").size; -} - -export function readableStreamHasBYOBReader(stream) { - const reader = $getByIdDirectPrivate(stream, "reader"); - return reader !== undefined && $isReadableStreamBYOBReader(reader); -} - -export function readableStreamHasDefaultReader(stream) { - const reader = $getByIdDirectPrivate(stream, "reader"); - return reader !== undefined && $isReadableStreamDefaultReader(reader); -} - -export function readableByteStreamControllerHandleQueueDrain(controller) { - $assert( - $getByIdDirectPrivate($getByIdDirectPrivate(controller, "controlledReadableStream"), "state") === $streamReadable, - ); - if (!$getByIdDirectPrivate(controller, "queue").size && $getByIdDirectPrivate(controller, "closeRequested")) - $readableStreamClose($getByIdDirectPrivate(controller, "controlledReadableStream")); - else $readableByteStreamControllerCallPullIfNeeded(controller); -} - -export function readableByteStreamControllerPull(controller) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - $assert($readableStreamHasDefaultReader(stream)); - if ($getByIdDirectPrivate(controller, "queue").content?.isNotEmpty()) { - const entry = $getByIdDirectPrivate(controller, "queue").content.shift(); - $getByIdDirectPrivate(controller, "queue").size -= entry.byteLength; - $readableByteStreamControllerHandleQueueDrain(controller); - let view; - try { - view = new Uint8Array(entry.buffer, entry.byteOffset, entry.byteLength); - } catch (error) { - return Promise.$reject(error); - } - return $createFulfilledPromise({ value: view, done: false }); - } - - if ($getByIdDirectPrivate(controller, "autoAllocateChunkSize") !== undefined) { - let buffer; - try { - buffer = $createUninitializedArrayBuffer($getByIdDirectPrivate(controller, "autoAllocateChunkSize")); - } catch (error) { - return Promise.$reject(error); - } - const pullIntoDescriptor = { - buffer, - byteOffset: 0, - byteLength: $getByIdDirectPrivate(controller, "autoAllocateChunkSize"), - bytesFilled: 0, - elementSize: 1, - ctor: Uint8Array, - readerType: "default", - }; - $getByIdDirectPrivate(controller, "pendingPullIntos").push(pullIntoDescriptor); - } - - const promise = $readableStreamAddReadRequest(stream); - $readableByteStreamControllerCallPullIfNeeded(controller); - return promise; -} - -export function readableByteStreamControllerShouldCallPull(controller) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - - if ($getByIdDirectPrivate(stream, "state") !== $streamReadable) return false; - if ($getByIdDirectPrivate(controller, "closeRequested")) return false; - if (!($getByIdDirectPrivate(controller, "started") > 0)) return false; - const reader = $getByIdDirectPrivate(stream, "reader"); - - if ( - reader && - ($getByIdDirectPrivate(reader, "readRequests")?.isNotEmpty() || !!$getByIdDirectPrivate(reader, "bunNativePtr")) - ) - return true; - if ( - $readableStreamHasBYOBReader(stream) && - $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readIntoRequests")?.isNotEmpty() - ) - return true; - if ($readableByteStreamControllerGetDesiredSize(controller) > 0) return true; - return false; -} - -export function readableByteStreamControllerCallPullIfNeeded(controller) { - if (!$readableByteStreamControllerShouldCallPull(controller)) return; - - if ($getByIdDirectPrivate(controller, "pulling")) { - $putByIdDirectPrivate(controller, "pullAgain", true); - return; - } - - $assert(!$getByIdDirectPrivate(controller, "pullAgain")); - $putByIdDirectPrivate(controller, "pulling", true); - $promiseInvokeOrNoop($getByIdDirectPrivate(controller, "underlyingByteSource"), "pull", [controller]).$then( - () => { - $putByIdDirectPrivate(controller, "pulling", false); - if ($getByIdDirectPrivate(controller, "pullAgain")) { - $putByIdDirectPrivate(controller, "pullAgain", false); - $readableByteStreamControllerCallPullIfNeeded(controller); - } - }, - error => { - if ( - $getByIdDirectPrivate($getByIdDirectPrivate(controller, "controlledReadableStream"), "state") === - $streamReadable - ) - $readableByteStreamControllerError(controller, error); - }, - ); -} - -export function transferBufferToCurrentRealm(buffer) { - // FIXME: Determine what should be done here exactly (what is already existing in current - // codebase and what has to be added). According to spec, Transfer operation should be - // performed in order to transfer buffer to current realm. For the moment, simply return - // received buffer. - return buffer; -} - -export function readableStreamReaderKind(reader) { - if (!!$getByIdDirectPrivate(reader, "readRequests")) return $getByIdDirectPrivate(reader, "bunNativePtr") ? 3 : 1; - - if (!!$getByIdDirectPrivate(reader, "readIntoRequests")) return 2; - - return 0; -} - -export function readableByteStreamControllerEnqueue(controller, chunk) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - $assert(!$getByIdDirectPrivate(controller, "closeRequested")); - $assert($getByIdDirectPrivate(stream, "state") === $streamReadable); - - switch ( - $getByIdDirectPrivate(stream, "reader") ? $readableStreamReaderKind($getByIdDirectPrivate(stream, "reader")) : 0 - ) { - /* default reader */ - case 1: { - if (!$getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests")?.isNotEmpty()) - $readableByteStreamControllerEnqueueChunk( - controller, - $transferBufferToCurrentRealm(chunk.buffer), - chunk.byteOffset, - chunk.byteLength, - ); - else { - $assert(!$getByIdDirectPrivate(controller, "queue").content.size()); - const transferredView = - chunk.constructor === Uint8Array ? chunk : new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength); - $readableStreamFulfillReadRequest(stream, transferredView, false); - } - break; - } - - /* BYOB */ - case 2: { - $readableByteStreamControllerEnqueueChunk( - controller, - $transferBufferToCurrentRealm(chunk.buffer), - chunk.byteOffset, - chunk.byteLength, - ); - $readableByteStreamControllerProcessPullDescriptors(controller); - break; - } - - /* NativeReader */ - case 3: { - // reader.$enqueueNative($getByIdDirectPrivate(reader, "bunNativePtr"), chunk); - - break; - } - - default: { - $assert(!$isReadableStreamLocked(stream)); - $readableByteStreamControllerEnqueueChunk( - controller, - $transferBufferToCurrentRealm(chunk.buffer), - chunk.byteOffset, - chunk.byteLength, - ); - break; - } - } -} - -// Spec name: readableByteStreamControllerEnqueueChunkToQueue. -export function readableByteStreamControllerEnqueueChunk(controller, buffer, byteOffset, byteLength) { - $getByIdDirectPrivate(controller, "queue").content.push({ - buffer: buffer, - byteOffset: byteOffset, - byteLength: byteLength, - }); - $getByIdDirectPrivate(controller, "queue").size += byteLength; -} - -export function readableByteStreamControllerRespondWithNewView(controller, view) { - $assert($getByIdDirectPrivate(controller, "pendingPullIntos").isNotEmpty()); - - let firstDescriptor = $getByIdDirectPrivate(controller, "pendingPullIntos").peek(); - - if (firstDescriptor.byteOffset + firstDescriptor.bytesFilled !== view.byteOffset) - throw new RangeError("Invalid value for view.byteOffset"); - - if (firstDescriptor.byteLength !== view.byteLength) throw new RangeError("Invalid value for view.byteLength"); - - firstDescriptor.buffer = view.buffer; - $readableByteStreamControllerRespondInternal(controller, view.byteLength); -} - -export function readableByteStreamControllerRespond(controller, bytesWritten) { - bytesWritten = $toNumber(bytesWritten); - - if (isNaN(bytesWritten) || bytesWritten === Infinity || bytesWritten < 0) - throw new RangeError("bytesWritten has an incorrect value"); - - $assert($getByIdDirectPrivate(controller, "pendingPullIntos").isNotEmpty()); - - $readableByteStreamControllerRespondInternal(controller, bytesWritten); -} - -export function readableByteStreamControllerRespondInternal(controller, bytesWritten) { - let firstDescriptor = $getByIdDirectPrivate(controller, "pendingPullIntos").peek(); - let stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - - if ($getByIdDirectPrivate(stream, "state") === $streamClosed) { - if (bytesWritten !== 0) throw new TypeError("bytesWritten is different from 0 even though stream is closed"); - $readableByteStreamControllerRespondInClosedState(controller, firstDescriptor); - } else { - $assert($getByIdDirectPrivate(stream, "state") === $streamReadable); - $readableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor); - } -} - -export function readableByteStreamControllerRespondInReadableState(controller, bytesWritten, pullIntoDescriptor) { - if (pullIntoDescriptor.bytesFilled + bytesWritten > pullIntoDescriptor.byteLength) - throw new RangeError("bytesWritten value is too great"); - - $assert( - $getByIdDirectPrivate(controller, "pendingPullIntos").isEmpty() || - $getByIdDirectPrivate(controller, "pendingPullIntos").peek() === pullIntoDescriptor, - ); - $readableByteStreamControllerInvalidateBYOBRequest(controller); - pullIntoDescriptor.bytesFilled += bytesWritten; - - if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) return; - - $readableByteStreamControllerShiftPendingDescriptor(controller); - const remainderSize = pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize; - - if (remainderSize > 0) { - const end = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; - const remainder = $cloneArrayBuffer(pullIntoDescriptor.buffer, end - remainderSize, remainderSize); - $readableByteStreamControllerEnqueueChunk(controller, remainder, 0, remainder.byteLength); - } - - pullIntoDescriptor.buffer = $transferBufferToCurrentRealm(pullIntoDescriptor.buffer); - pullIntoDescriptor.bytesFilled -= remainderSize; - $readableByteStreamControllerCommitDescriptor( - $getByIdDirectPrivate(controller, "controlledReadableStream"), - pullIntoDescriptor, - ); - $readableByteStreamControllerProcessPullDescriptors(controller); -} - -export function readableByteStreamControllerRespondInClosedState(controller, firstDescriptor) { - firstDescriptor.buffer = $transferBufferToCurrentRealm(firstDescriptor.buffer); - $assert(firstDescriptor.bytesFilled === 0); - - if ($readableStreamHasBYOBReader($getByIdDirectPrivate(controller, "controlledReadableStream"))) { - while ( - $getByIdDirectPrivate( - $getByIdDirectPrivate($getByIdDirectPrivate(controller, "controlledReadableStream"), "reader"), - "readIntoRequests", - )?.isNotEmpty() - ) { - let pullIntoDescriptor = $readableByteStreamControllerShiftPendingDescriptor(controller); - $readableByteStreamControllerCommitDescriptor( - $getByIdDirectPrivate(controller, "controlledReadableStream"), - pullIntoDescriptor, - ); - } - } -} - -// Spec name: readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue (shortened for readability). -export function readableByteStreamControllerProcessPullDescriptors(controller) { - $assert(!$getByIdDirectPrivate(controller, "closeRequested")); - while ($getByIdDirectPrivate(controller, "pendingPullIntos").isNotEmpty()) { - if ($getByIdDirectPrivate(controller, "queue").size === 0) return; - let pullIntoDescriptor = $getByIdDirectPrivate(controller, "pendingPullIntos").peek(); - if ($readableByteStreamControllerFillDescriptorFromQueue(controller, pullIntoDescriptor)) { - $readableByteStreamControllerShiftPendingDescriptor(controller); - $readableByteStreamControllerCommitDescriptor( - $getByIdDirectPrivate(controller, "controlledReadableStream"), - pullIntoDescriptor, - ); - } - } -} - -// Spec name: readableByteStreamControllerFillPullIntoDescriptorFromQueue (shortened for readability). -export function readableByteStreamControllerFillDescriptorFromQueue(controller, pullIntoDescriptor) { - const currentAlignedBytes = - pullIntoDescriptor.bytesFilled - (pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize); - const maxBytesToCopy = - $getByIdDirectPrivate(controller, "queue").size < pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled - ? $getByIdDirectPrivate(controller, "queue").size - : pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled; - const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy; - const maxAlignedBytes = maxBytesFilled - (maxBytesFilled % pullIntoDescriptor.elementSize); - let totalBytesToCopyRemaining = maxBytesToCopy; - let ready = false; - - if (maxAlignedBytes > currentAlignedBytes) { - totalBytesToCopyRemaining = maxAlignedBytes - pullIntoDescriptor.bytesFilled; - ready = true; - } - - while (totalBytesToCopyRemaining > 0) { - let headOfQueue = $getByIdDirectPrivate(controller, "queue").content.peek(); - const bytesToCopy = - totalBytesToCopyRemaining < headOfQueue.byteLength ? totalBytesToCopyRemaining : headOfQueue.byteLength; - // Copy appropriate part of pullIntoDescriptor.buffer to headOfQueue.buffer. - // Remark: this implementation is not completely aligned on the definition of CopyDataBlockBytes - // operation of ECMAScript (the case of Shared Data Block is not considered here, but it doesn't seem to be an issue). - const destStart = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; - // FIXME: As indicated in comments of bug 172717, access to set is not safe. However, using prototype.$set.$call does - // not work ($set is undefined). A safe way to do that is needed. - new Uint8Array(pullIntoDescriptor.buffer).set( - new Uint8Array(headOfQueue.buffer, headOfQueue.byteOffset, bytesToCopy), - destStart, - ); - - if (headOfQueue.byteLength === bytesToCopy) $getByIdDirectPrivate(controller, "queue").content.shift(); - else { - headOfQueue.byteOffset += bytesToCopy; - headOfQueue.byteLength -= bytesToCopy; - } - - $getByIdDirectPrivate(controller, "queue").size -= bytesToCopy; - $assert( - $getByIdDirectPrivate(controller, "pendingPullIntos").isEmpty() || - $getByIdDirectPrivate(controller, "pendingPullIntos").peek() === pullIntoDescriptor, - ); - $readableByteStreamControllerInvalidateBYOBRequest(controller); - pullIntoDescriptor.bytesFilled += bytesToCopy; - totalBytesToCopyRemaining -= bytesToCopy; - } - - if (!ready) { - $assert($getByIdDirectPrivate(controller, "queue").size === 0); - $assert(pullIntoDescriptor.bytesFilled > 0); - $assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize); - } - - return ready; -} - -// Spec name: readableByteStreamControllerShiftPendingPullInto (renamed for consistency). -export function readableByteStreamControllerShiftPendingDescriptor(controller) { - let descriptor = $getByIdDirectPrivate(controller, "pendingPullIntos").shift(); - $readableByteStreamControllerInvalidateBYOBRequest(controller); - return descriptor; -} - -export function readableByteStreamControllerInvalidateBYOBRequest(controller) { - if ($getByIdDirectPrivate(controller, "byobRequest") === undefined) return; - const byobRequest = $getByIdDirectPrivate(controller, "byobRequest"); - $putByIdDirectPrivate(byobRequest, "associatedReadableByteStreamController", undefined); - $putByIdDirectPrivate(byobRequest, "view", undefined); - $putByIdDirectPrivate(controller, "byobRequest", undefined); -} - -// Spec name: readableByteStreamControllerCommitPullIntoDescriptor (shortened for readability). -export function readableByteStreamControllerCommitDescriptor(stream, pullIntoDescriptor) { - $assert($getByIdDirectPrivate(stream, "state") !== $streamErrored); - let done = false; - if ($getByIdDirectPrivate(stream, "state") === $streamClosed) { - $assert(!pullIntoDescriptor.bytesFilled); - done = true; - } - let filledView = $readableByteStreamControllerConvertDescriptor(pullIntoDescriptor); - if (pullIntoDescriptor.readerType === "default") $readableStreamFulfillReadRequest(stream, filledView, done); - else { - $assert(pullIntoDescriptor.readerType === "byob"); - $readableStreamFulfillReadIntoRequest(stream, filledView, done); - } -} - -// Spec name: readableByteStreamControllerConvertPullIntoDescriptor (shortened for readability). -export function readableByteStreamControllerConvertDescriptor(pullIntoDescriptor) { - $assert(pullIntoDescriptor.bytesFilled <= pullIntoDescriptor.byteLength); - $assert(pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize === 0); - - return new pullIntoDescriptor.ctor( - pullIntoDescriptor.buffer, - pullIntoDescriptor.byteOffset, - pullIntoDescriptor.bytesFilled / pullIntoDescriptor.elementSize, - ); -} - -export function readableStreamFulfillReadIntoRequest(stream, chunk, done) { - const readIntoRequest = $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readIntoRequests").shift(); - $fulfillPromise(readIntoRequest, { value: chunk, done: done }); -} - -export function readableStreamBYOBReaderRead(reader, view) { - const stream = $getByIdDirectPrivate(reader, "ownerReadableStream"); - $assert(!!stream); - - $putByIdDirectPrivate(stream, "disturbed", true); - if ($getByIdDirectPrivate(stream, "state") === $streamErrored) - return Promise.$reject($getByIdDirectPrivate(stream, "storedError")); - - return $readableByteStreamControllerPullInto($getByIdDirectPrivate(stream, "readableStreamController"), view); -} - -export function readableByteStreamControllerPullInto(controller, view) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - let elementSize = 1; - // Spec describes that in the case where view is a TypedArray, elementSize - // should be set to the size of an element (e.g. 2 for UInt16Array). For - // DataView, BYTES_PER_ELEMENT is undefined, contrary to the same property - // for TypedArrays. - // FIXME: Getting BYTES_PER_ELEMENT like this is not safe (property is read-only - // but can be modified if the prototype is redefined). A safe way of getting - // it would be to determine which type of ArrayBufferView view is an instance - // of based on typed arrays private variables. However, this is not possible due - // to bug 167697, which prevents access to typed arrays through their private - // names unless public name has already been met before. - if (view.BYTES_PER_ELEMENT !== undefined) elementSize = view.BYTES_PER_ELEMENT; - - // FIXME: Getting constructor like this is not safe. A safe way of getting - // it would be to determine which type of ArrayBufferView view is an instance - // of, and to assign appropriate constructor based on this (e.g. ctor = - // $Uint8Array). However, this is not possible due to bug 167697, which - // prevents access to typed arrays through their private names unless public - // name has already been met before. - const ctor = view.constructor; - - const pullIntoDescriptor = { - buffer: view.buffer, - byteOffset: view.byteOffset, - byteLength: view.byteLength, - bytesFilled: 0, - elementSize, - ctor, - readerType: "byob", - }; - - var pending = $getByIdDirectPrivate(controller, "pendingPullIntos"); - if (pending?.isNotEmpty()) { - pullIntoDescriptor.buffer = $transferBufferToCurrentRealm(pullIntoDescriptor.buffer); - pending.push(pullIntoDescriptor); - return $readableStreamAddReadIntoRequest(stream); - } - - if ($getByIdDirectPrivate(stream, "state") === $streamClosed) { - const emptyView = new ctor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, 0); - return $createFulfilledPromise({ value: emptyView, done: true }); - } - - if ($getByIdDirectPrivate(controller, "queue").size > 0) { - if ($readableByteStreamControllerFillDescriptorFromQueue(controller, pullIntoDescriptor)) { - const filledView = $readableByteStreamControllerConvertDescriptor(pullIntoDescriptor); - $readableByteStreamControllerHandleQueueDrain(controller); - return $createFulfilledPromise({ value: filledView, done: false }); - } - if ($getByIdDirectPrivate(controller, "closeRequested")) { - const e = $makeTypeError("Closing stream has been requested"); - $readableByteStreamControllerError(controller, e); - return Promise.$reject(e); - } - } - - pullIntoDescriptor.buffer = $transferBufferToCurrentRealm(pullIntoDescriptor.buffer); - $getByIdDirectPrivate(controller, "pendingPullIntos").push(pullIntoDescriptor); - const promise = $readableStreamAddReadIntoRequest(stream); - $readableByteStreamControllerCallPullIfNeeded(controller); - return promise; -} - -export function readableStreamAddReadIntoRequest(stream) { - $assert($isReadableStreamBYOBReader($getByIdDirectPrivate(stream, "reader"))); - $assert( - $getByIdDirectPrivate(stream, "state") === $streamReadable || - $getByIdDirectPrivate(stream, "state") === $streamClosed, - ); - - const readRequest = $newPromise(); - $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readIntoRequests").push(readRequest); - - return readRequest; -} diff --git a/src/bun.js/builtins/ts/ReadableStream.ts b/src/bun.js/builtins/ts/ReadableStream.ts deleted file mode 100644 index 613f869e5..000000000 --- a/src/bun.js/builtins/ts/ReadableStream.ts +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. - * Copyright (C) 2015 Igalia. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeReadableStream(this: any, underlyingSource: UnderlyingSource, strategy: any) { - if (underlyingSource === undefined) - underlyingSource = { $bunNativeType: 0, $bunNativePtr: 0, $lazy: false } as UnderlyingSource; - if (strategy === undefined) strategy = {}; - - if (!$isObject(underlyingSource)) throw new TypeError("ReadableStream constructor takes an object as first argument"); - - if (strategy !== undefined && !$isObject(strategy)) - throw new TypeError("ReadableStream constructor takes an object as second argument, if any"); - - $putByIdDirectPrivate(this, "state", $streamReadable); - - $putByIdDirectPrivate(this, "reader", undefined); - - $putByIdDirectPrivate(this, "storedError", undefined); - - $putByIdDirectPrivate(this, "disturbed", false); - - // Initialized with null value to enable distinction with undefined case. - $putByIdDirectPrivate(this, "readableStreamController", null); - $putByIdDirectPrivate(this, "bunNativeType", $getByIdDirectPrivate(underlyingSource, "bunNativeType") ?? 0); - $putByIdDirectPrivate(this, "bunNativePtr", $getByIdDirectPrivate(underlyingSource, "bunNativePtr") ?? 0); - - const isDirect = underlyingSource.type === "direct"; - // direct streams are always lazy - const isUnderlyingSourceLazy = !!underlyingSource.$lazy; - const isLazy = isDirect || isUnderlyingSourceLazy; - - // FIXME: We should introduce https://streams.spec.whatwg.org/#create-readable-stream. - // For now, we emulate this with underlyingSource with private properties. - if ($getByIdDirectPrivate(underlyingSource, "pull") !== undefined && !isLazy) { - const size = $getByIdDirectPrivate(strategy, "size"); - const highWaterMark = $getByIdDirectPrivate(strategy, "highWaterMark"); - $putByIdDirectPrivate(this, "highWaterMark", highWaterMark); - $putByIdDirectPrivate(this, "underlyingSource", undefined); - $setupReadableStreamDefaultController( - this, - underlyingSource, - size, - highWaterMark !== undefined ? highWaterMark : 1, - $getByIdDirectPrivate(underlyingSource, "start"), - $getByIdDirectPrivate(underlyingSource, "pull"), - $getByIdDirectPrivate(underlyingSource, "cancel"), - ); - - return this; - } - if (isDirect) { - $putByIdDirectPrivate(this, "underlyingSource", underlyingSource); - $putByIdDirectPrivate(this, "highWaterMark", $getByIdDirectPrivate(strategy, "highWaterMark")); - $putByIdDirectPrivate(this, "start", () => $createReadableStreamController(this, underlyingSource, strategy)); - } else if (isLazy) { - const autoAllocateChunkSize = underlyingSource.autoAllocateChunkSize; - $putByIdDirectPrivate(this, "highWaterMark", undefined); - $putByIdDirectPrivate(this, "underlyingSource", undefined); - $putByIdDirectPrivate( - this, - "highWaterMark", - autoAllocateChunkSize || $getByIdDirectPrivate(strategy, "highWaterMark"), - ); - - $putByIdDirectPrivate(this, "start", () => { - const instance = $lazyLoadStream(this, autoAllocateChunkSize); - if (instance) { - $createReadableStreamController(this, instance, strategy); - } - }); - } else { - $putByIdDirectPrivate(this, "underlyingSource", undefined); - $putByIdDirectPrivate(this, "highWaterMark", $getByIdDirectPrivate(strategy, "highWaterMark")); - $putByIdDirectPrivate(this, "start", undefined); - $createReadableStreamController(this, underlyingSource, strategy); - } - - return this; -} - -$linkTimeConstant; -export function readableStreamToArray(stream: ReadableStream): Promise<unknown[]> { - // this is a direct stream - var underlyingSource = $getByIdDirectPrivate(stream, "underlyingSource"); - if (underlyingSource !== undefined) { - return $readableStreamToArrayDirect(stream, underlyingSource); - } - - return $readableStreamIntoArray(stream); -} - -$linkTimeConstant; -export function readableStreamToText(stream: ReadableStream): Promise<string> { - // this is a direct stream - var underlyingSource = $getByIdDirectPrivate(stream, "underlyingSource"); - if (underlyingSource !== undefined) { - return $readableStreamToTextDirect(stream, underlyingSource); - } - - return $readableStreamIntoText(stream); -} - -$linkTimeConstant; -export function readableStreamToArrayBuffer(stream: ReadableStream<ArrayBuffer>): Promise<ArrayBuffer> | ArrayBuffer { - // this is a direct stream - var underlyingSource = $getByIdDirectPrivate(stream, "underlyingSource"); - - if (underlyingSource !== undefined) { - return $readableStreamToArrayBufferDirect(stream, underlyingSource); - } - - var array = Bun.readableStreamToArray(stream); - if ($isPromise(array)) { - return array.$then(Bun.concatArrayBuffers); - } - - return Bun.concatArrayBuffers(array); -} - -$linkTimeConstant; -export function readableStreamToJSON(stream: ReadableStream): unknown { - return Bun.readableStreamToText(stream).$then(globalThis.JSON.parse); -} - -$linkTimeConstant; -export function readableStreamToBlob(stream: ReadableStream): Promise<Blob> { - return Promise.resolve(Bun.readableStreamToArray(stream)).$then(array => new Blob(array)); -} - -$linkTimeConstant; -export function consumeReadableStream(nativePtr, nativeType, inputStream) { - const symbol = globalThis.Symbol.for("Bun.consumeReadableStreamPrototype"); - var cached = globalThis[symbol]; - if (!cached) { - cached = globalThis[symbol] = []; - } - var Prototype = cached[nativeType]; - if (Prototype === undefined) { - var [doRead, doError, doReadMany, doClose, onClose, deinit] = - globalThis[globalThis.Symbol.for("Bun.lazy")](nativeType); - - Prototype = class NativeReadableStreamSink { - handleError: any; - handleClosed: any; - processResult: any; - - constructor(reader, ptr) { - this.#ptr = ptr; - this.#reader = reader; - this.#didClose = false; - - this.handleError = this._handleError.bind(this); - this.handleClosed = this._handleClosed.bind(this); - this.processResult = this._processResult.bind(this); - - reader.closed.then(this.handleClosed, this.handleError); - } - - _handleClosed() { - if (this.#didClose) return; - this.#didClose = true; - var ptr = this.#ptr; - this.#ptr = 0; - doClose(ptr); - deinit(ptr); - } - - _handleError(error) { - if (this.#didClose) return; - this.#didClose = true; - var ptr = this.#ptr; - this.#ptr = 0; - doError(ptr, error); - deinit(ptr); - } - - #ptr; - #didClose = false; - #reader; - - _handleReadMany({ value, done, size }) { - if (done) { - this.handleClosed(); - return; - } - - if (this.#didClose) return; - - doReadMany(this.#ptr, value, done, size); - } - - read() { - if (!this.#ptr) return $throwTypeError("ReadableStreamSink is already closed"); - - return this.processResult(this.#reader.read()); - } - - _processResult(result) { - if (result && $isPromise(result)) { - const flags = $getPromiseInternalField(result, $promiseFieldFlags); - if (flags & $promiseStateFulfilled) { - const fulfilledValue = $getPromiseInternalField(result, $promiseFieldReactionsOrResult); - if (fulfilledValue) { - result = fulfilledValue; - } - } - } - - if (result && $isPromise(result)) { - result.then(this.processResult, this.handleError); - return null; - } - - if (result.done) { - this.handleClosed(); - return 0; - } else if (result.value) { - return result.value; - } else { - return -1; - } - } - - readMany() { - if (!this.#ptr) return $throwTypeError("ReadableStreamSink is already closed"); - return this.processResult(this.#reader.readMany()); - } - }; - - const minlength = nativeType + 1; - if (cached.length < minlength) { - cached.length = minlength; - } - $putByValDirect(cached, nativeType, Prototype); - } - - if ($isReadableStreamLocked(inputStream)) { - throw new TypeError("Cannot start reading from a locked stream"); - } - - return new Prototype(inputStream.getReader(), nativePtr); -} - -$linkTimeConstant; -export function createEmptyReadableStream() { - var stream = new ReadableStream({ - pull() {}, - } as any); - $readableStreamClose(stream); - return stream; -} - -$linkTimeConstant; -export function createNativeReadableStream(nativePtr, nativeType, autoAllocateChunkSize) { - return new ReadableStream({ - $lazy: true, - $bunNativeType: nativeType, - $bunNativePtr: nativePtr, - autoAllocateChunkSize: autoAllocateChunkSize, - }); -} - -export function cancel(this, reason) { - if (!$isReadableStream(this)) return Promise.$reject($makeThisTypeError("ReadableStream", "cancel")); - - if ($isReadableStreamLocked(this)) return Promise.$reject($makeTypeError("ReadableStream is locked")); - - return $readableStreamCancel(this, reason); -} - -export function getReader(this, options) { - if (!$isReadableStream(this)) throw $makeThisTypeError("ReadableStream", "getReader"); - - const mode = $toDictionary(options, {}, "ReadableStream.getReader takes an object as first argument").mode; - if (mode === undefined) { - var start_ = $getByIdDirectPrivate(this, "start"); - if (start_) { - $putByIdDirectPrivate(this, "start", undefined); - start_(); - } - - return new ReadableStreamDefaultReader(this); - } - // String conversion is required by spec, hence double equals. - if (mode == "byob") { - return new ReadableStreamBYOBReader(this); - } - - throw new TypeError("Invalid mode is specified"); -} - -export function pipeThrough(this, streams, options) { - const transforms = streams; - - const readable = transforms["readable"]; - if (!$isReadableStream(readable)) throw $makeTypeError("readable should be ReadableStream"); - - const writable = transforms["writable"]; - const internalWritable = $getInternalWritableStream(writable); - if (!$isWritableStream(internalWritable)) throw $makeTypeError("writable should be WritableStream"); - - let preventClose = false; - let preventAbort = false; - let preventCancel = false; - let signal; - if (!$isUndefinedOrNull(options)) { - if (!$isObject(options)) throw $makeTypeError("options must be an object"); - - preventAbort = !!options["preventAbort"]; - preventCancel = !!options["preventCancel"]; - preventClose = !!options["preventClose"]; - - signal = options["signal"]; - if (signal !== undefined && !$isAbortSignal(signal)) throw $makeTypeError("options.signal must be AbortSignal"); - } - - if (!$isReadableStream(this)) throw $makeThisTypeError("ReadableStream", "pipeThrough"); - - if ($isReadableStreamLocked(this)) throw $makeTypeError("ReadableStream is locked"); - - if ($isWritableStreamLocked(internalWritable)) throw $makeTypeError("WritableStream is locked"); - - $readableStreamPipeToWritableStream(this, internalWritable, preventClose, preventAbort, preventCancel, signal); - - return readable; -} - -export function pipeTo(this, destination) { - if (!$isReadableStream(this)) return Promise.$reject($makeThisTypeError("ReadableStream", "pipeTo")); - - if ($isReadableStreamLocked(this)) return Promise.$reject($makeTypeError("ReadableStream is locked")); - - // FIXME: https://bugs.webkit.org/show_bug.cgi?id=159869. - // Built-in generator should be able to parse function signature to compute the function length correctly. - let options = $argument(1); - - let preventClose = false; - let preventAbort = false; - let preventCancel = false; - let signal; - if (!$isUndefinedOrNull(options)) { - if (!$isObject(options)) return Promise.$reject($makeTypeError("options must be an object")); - - try { - preventAbort = !!options["preventAbort"]; - preventCancel = !!options["preventCancel"]; - preventClose = !!options["preventClose"]; - - signal = options["signal"]; - } catch (e) { - return Promise.$reject(e); - } - - if (signal !== undefined && !$isAbortSignal(signal)) - return Promise.$reject(new TypeError("options.signal must be AbortSignal")); - } - - const internalDestination = $getInternalWritableStream(destination); - if (!$isWritableStream(internalDestination)) - return Promise.$reject(new TypeError("ReadableStream pipeTo requires a WritableStream")); - - if ($isWritableStreamLocked(internalDestination)) return Promise.$reject(new TypeError("WritableStream is locked")); - - return $readableStreamPipeToWritableStream( - this, - internalDestination, - preventClose, - preventAbort, - preventCancel, - signal, - ); -} - -export function tee(this) { - if (!$isReadableStream(this)) throw $makeThisTypeError("ReadableStream", "tee"); - - return $readableStreamTee(this, false); -} - -$getter; -export function locked(this) { - if (!$isReadableStream(this)) throw $makeGetterTypeError("ReadableStream", "locked"); - - return $isReadableStreamLocked(this); -} - -export function values(this, options) { - var prototype = ReadableStream.prototype; - $readableStreamDefineLazyIterators(prototype); - return prototype.values.$call(this, options); -} - -$linkTimeConstant; -export function lazyAsyncIterator(this) { - var prototype = ReadableStream.prototype; - $readableStreamDefineLazyIterators(prototype); - return prototype[globalThis.Symbol.asyncIterator].$call(this); -} diff --git a/src/bun.js/builtins/ts/ReadableStreamBYOBReader.ts b/src/bun.js/builtins/ts/ReadableStreamBYOBReader.ts deleted file mode 100644 index 5ebfddb19..000000000 --- a/src/bun.js/builtins/ts/ReadableStreamBYOBReader.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2017 Canon Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeReadableStreamBYOBReader(this, stream) { - if (!$isReadableStream(stream)) throw new TypeError("ReadableStreamBYOBReader needs a ReadableStream"); - if (!$isReadableByteStreamController($getByIdDirectPrivate(stream, "readableStreamController"))) - throw new TypeError("ReadableStreamBYOBReader needs a ReadableByteStreamController"); - if ($isReadableStreamLocked(stream)) throw new TypeError("ReadableStream is locked"); - - $readableStreamReaderGenericInitialize(this, stream); - $putByIdDirectPrivate(this, "readIntoRequests", $createFIFO()); - - return this; -} - -export function cancel(this, reason) { - if (!$isReadableStreamBYOBReader(this)) - return Promise.$reject($makeThisTypeError("ReadableStreamBYOBReader", "cancel")); - - if (!$getByIdDirectPrivate(this, "ownerReadableStream")) - return Promise.$reject($makeTypeError("cancel() called on a reader owned by no readable stream")); - - return $readableStreamReaderGenericCancel(this, reason); -} - -export function read(this, view: DataView) { - if (!$isReadableStreamBYOBReader(this)) - return Promise.$reject($makeThisTypeError("ReadableStreamBYOBReader", "read")); - - if (!$getByIdDirectPrivate(this, "ownerReadableStream")) - return Promise.$reject($makeTypeError("read() called on a reader owned by no readable stream")); - - if (!$isObject(view)) return Promise.$reject($makeTypeError("Provided view is not an object")); - - if (!ArrayBuffer.$isView(view)) return Promise.$reject($makeTypeError("Provided view is not an ArrayBufferView")); - - if (view.byteLength === 0) return Promise.$reject($makeTypeError("Provided view cannot have a 0 byteLength")); - - return $readableStreamBYOBReaderRead(this, view); -} - -export function releaseLock(this) { - if (!$isReadableStreamBYOBReader(this)) throw $makeThisTypeError("ReadableStreamBYOBReader", "releaseLock"); - - if (!$getByIdDirectPrivate(this, "ownerReadableStream")) return; - - if ($getByIdDirectPrivate(this, "readIntoRequests")?.isNotEmpty()) - throw new TypeError("There are still pending read requests, cannot release the lock"); - - $readableStreamReaderGenericRelease(this); -} - -$getter; -export function closed(this) { - if (!$isReadableStreamBYOBReader(this)) - return Promise.$reject($makeGetterTypeError("ReadableStreamBYOBReader", "closed")); - - return $getByIdDirectPrivate(this, "closedPromiseCapability").$promise; -} diff --git a/src/bun.js/builtins/ts/ReadableStreamBYOBRequest.ts b/src/bun.js/builtins/ts/ReadableStreamBYOBRequest.ts deleted file mode 100644 index 1354f9349..000000000 --- a/src/bun.js/builtins/ts/ReadableStreamBYOBRequest.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2017 Canon Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeReadableStreamBYOBRequest(this, controller, view) { - if (arguments.length !== 3 && arguments[2] !== $isReadableStream) - throw new TypeError("ReadableStreamBYOBRequest constructor should not be called directly"); - - return $privateInitializeReadableStreamBYOBRequest.$call(this, controller, view); -} - -export function respond(this, bytesWritten) { - if (!$isReadableStreamBYOBRequest(this)) throw $makeThisTypeError("ReadableStreamBYOBRequest", "respond"); - - if ($getByIdDirectPrivate(this, "associatedReadableByteStreamController") === undefined) - throw new TypeError("ReadableStreamBYOBRequest.associatedReadableByteStreamController is undefined"); - - return $readableByteStreamControllerRespond( - $getByIdDirectPrivate(this, "associatedReadableByteStreamController"), - bytesWritten, - ); -} - -export function respondWithNewView(this, view) { - if (!$isReadableStreamBYOBRequest(this)) throw $makeThisTypeError("ReadableStreamBYOBRequest", "respond"); - - if ($getByIdDirectPrivate(this, "associatedReadableByteStreamController") === undefined) - throw new TypeError("ReadableStreamBYOBRequest.associatedReadableByteStreamController is undefined"); - - if (!$isObject(view)) throw new TypeError("Provided view is not an object"); - - if (!ArrayBuffer.$isView(view)) throw new TypeError("Provided view is not an ArrayBufferView"); - - return $readableByteStreamControllerRespondWithNewView( - $getByIdDirectPrivate(this, "associatedReadableByteStreamController"), - view, - ); -} - -$getter; -export function view(this) { - if (!$isReadableStreamBYOBRequest(this)) throw $makeGetterTypeError("ReadableStreamBYOBRequest", "view"); - - return $getByIdDirectPrivate(this, "view"); -} diff --git a/src/bun.js/builtins/ts/ReadableStreamDefaultController.ts b/src/bun.js/builtins/ts/ReadableStreamDefaultController.ts deleted file mode 100644 index 912cd1acb..000000000 --- a/src/bun.js/builtins/ts/ReadableStreamDefaultController.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeReadableStreamDefaultController(this, stream, underlyingSource, size, highWaterMark) { - if (arguments.length !== 5 && arguments[4] !== $isReadableStream) - throw new TypeError("ReadableStreamDefaultController constructor should not be called directly"); - - return $privateInitializeReadableStreamDefaultController.$call(this, stream, underlyingSource, size, highWaterMark); -} - -export function enqueue(this, chunk) { - if (!$isReadableStreamDefaultController(this)) throw $makeThisTypeError("ReadableStreamDefaultController", "enqueue"); - - if (!$readableStreamDefaultControllerCanCloseOrEnqueue(this)) - throw new TypeError("ReadableStreamDefaultController is not in a state where chunk can be enqueued"); - - return $readableStreamDefaultControllerEnqueue(this, chunk); -} - -export function error(this, err) { - if (!$isReadableStreamDefaultController(this)) throw $makeThisTypeError("ReadableStreamDefaultController", "error"); - - $readableStreamDefaultControllerError(this, err); -} - -export function close(this) { - if (!$isReadableStreamDefaultController(this)) throw $makeThisTypeError("ReadableStreamDefaultController", "close"); - - if (!$readableStreamDefaultControllerCanCloseOrEnqueue(this)) - throw new TypeError("ReadableStreamDefaultController is not in a state where it can be closed"); - - $readableStreamDefaultControllerClose(this); -} - -$getter; -export function desiredSize(this) { - if (!$isReadableStreamDefaultController(this)) - throw $makeGetterTypeError("ReadableStreamDefaultController", "desiredSize"); - - return $readableStreamDefaultControllerGetDesiredSize(this); -} diff --git a/src/bun.js/builtins/ts/ReadableStreamDefaultReader.ts b/src/bun.js/builtins/ts/ReadableStreamDefaultReader.ts deleted file mode 100644 index ecd553ed5..000000000 --- a/src/bun.js/builtins/ts/ReadableStreamDefaultReader.ts +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeReadableStreamDefaultReader(this, stream) { - if (!$isReadableStream(stream)) throw new TypeError("ReadableStreamDefaultReader needs a ReadableStream"); - if ($isReadableStreamLocked(stream)) throw new TypeError("ReadableStream is locked"); - - $readableStreamReaderGenericInitialize(this, stream); - $putByIdDirectPrivate(this, "readRequests", $createFIFO()); - - return this; -} - -export function cancel(this, reason) { - if (!$isReadableStreamDefaultReader(this)) - return Promise.$reject($makeThisTypeError("ReadableStreamDefaultReader", "cancel")); - - if (!$getByIdDirectPrivate(this, "ownerReadableStream")) - return Promise.$reject(new TypeError("cancel() called on a reader owned by no readable stream")); - - return $readableStreamReaderGenericCancel(this, reason); -} - -export function readMany(this) { - if (!$isReadableStreamDefaultReader(this)) - throw new TypeError("ReadableStreamDefaultReader.readMany() should not be called directly"); - - const stream = $getByIdDirectPrivate(this, "ownerReadableStream"); - if (!stream) throw new TypeError("readMany() called on a reader owned by no readable stream"); - - const state = $getByIdDirectPrivate(stream, "state"); - $putByIdDirectPrivate(stream, "disturbed", true); - if (state === $streamClosed) return { value: [], size: 0, done: true }; - else if (state === $streamErrored) { - throw $getByIdDirectPrivate(stream, "storedError"); - } - - var controller = $getByIdDirectPrivate(stream, "readableStreamController"); - var queue = $getByIdDirectPrivate(controller, "queue"); - - if (!queue) { - // This is a ReadableStream direct controller implemented in JS - // It hasn't been started yet. - return controller.$pull(controller).$then(function ({ done, value }) { - return done ? { done: true, value: [], size: 0 } : { value: [value], size: 1, done: false }; - }); - } - - const content = queue.content; - var size = queue.size; - var values = content.toArray(false); - - var length = values.length; - - if (length > 0) { - var outValues = $newArrayWithSize(length); - if ($isReadableByteStreamController(controller)) { - { - const buf = values[0]; - if (!(ArrayBuffer.$isView(buf) || buf instanceof ArrayBuffer)) { - $putByValDirect(outValues, 0, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)); - } else { - $putByValDirect(outValues, 0, buf); - } - } - - for (var i = 1; i < length; i++) { - const buf = values[i]; - if (!(ArrayBuffer.$isView(buf) || buf instanceof ArrayBuffer)) { - $putByValDirect(outValues, i, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)); - } else { - $putByValDirect(outValues, i, buf); - } - } - } else { - $putByValDirect(outValues, 0, values[0].value); - for (var i = 1; i < length; i++) { - $putByValDirect(outValues, i, values[i].value); - } - } - - $resetQueue($getByIdDirectPrivate(controller, "queue")); - - if ($getByIdDirectPrivate(controller, "closeRequested")) - $readableStreamClose($getByIdDirectPrivate(controller, "controlledReadableStream")); - else if ($isReadableStreamDefaultController(controller)) - $readableStreamDefaultControllerCallPullIfNeeded(controller); - else if ($isReadableByteStreamController(controller)) $readableByteStreamControllerCallPullIfNeeded(controller); - - return { value: outValues, size, done: false }; - } - - var onPullMany = result => { - if (result.done) { - return { value: [], size: 0, done: true }; - } - var controller = $getByIdDirectPrivate(stream, "readableStreamController"); - - var queue = $getByIdDirectPrivate(controller, "queue"); - var value = [result.value].concat(queue.content.toArray(false)); - var length = value.length; - - if ($isReadableByteStreamController(controller)) { - for (var i = 0; i < length; i++) { - const buf = value[i]; - if (!(ArrayBuffer.$isView(buf) || buf instanceof ArrayBuffer)) { - const { buffer, byteOffset, byteLength } = buf; - $putByValDirect(value, i, new Uint8Array(buffer, byteOffset, byteLength)); - } - } - } else { - for (var i = 1; i < length; i++) { - $putByValDirect(value, i, value[i].value); - } - } - - var size = queue.size; - $resetQueue(queue); - - if ($getByIdDirectPrivate(controller, "closeRequested")) - $readableStreamClose($getByIdDirectPrivate(controller, "controlledReadableStream")); - else if ($isReadableStreamDefaultController(controller)) - $readableStreamDefaultControllerCallPullIfNeeded(controller); - else if ($isReadableByteStreamController(controller)) $readableByteStreamControllerCallPullIfNeeded(controller); - - return { value: value, size: size, done: false }; - }; - - var pullResult = controller.$pull(controller); - if (pullResult && $isPromise(pullResult)) { - return pullResult.$then(onPullMany); - } - - return onPullMany(pullResult); -} - -export function read(this) { - if (!$isReadableStreamDefaultReader(this)) - return Promise.$reject($makeThisTypeError("ReadableStreamDefaultReader", "read")); - if (!$getByIdDirectPrivate(this, "ownerReadableStream")) - return Promise.$reject(new TypeError("read() called on a reader owned by no readable stream")); - - return $readableStreamDefaultReaderRead(this); -} - -export function releaseLock(this) { - if (!$isReadableStreamDefaultReader(this)) throw $makeThisTypeError("ReadableStreamDefaultReader", "releaseLock"); - - if (!$getByIdDirectPrivate(this, "ownerReadableStream")) return; - - if ($getByIdDirectPrivate(this, "readRequests")?.isNotEmpty()) - throw new TypeError("There are still pending read requests, cannot release the lock"); - - $readableStreamReaderGenericRelease(this); -} - -$getter; -export function closed(this) { - if (!$isReadableStreamDefaultReader(this)) - return Promise.$reject($makeGetterTypeError("ReadableStreamDefaultReader", "closed")); - - return $getByIdDirectPrivate(this, "closedPromiseCapability").$promise; -} diff --git a/src/bun.js/builtins/ts/ReadableStreamInternals.ts b/src/bun.js/builtins/ts/ReadableStreamInternals.ts deleted file mode 100644 index 0c4e816f4..000000000 --- a/src/bun.js/builtins/ts/ReadableStreamInternals.ts +++ /dev/null @@ -1,1799 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. All rights reserved. - * Copyright (C) 2015 Igalia. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// @internal - -export function readableStreamReaderGenericInitialize(reader, stream) { - $putByIdDirectPrivate(reader, "ownerReadableStream", stream); - $putByIdDirectPrivate(stream, "reader", reader); - if ($getByIdDirectPrivate(stream, "state") === $streamReadable) - $putByIdDirectPrivate(reader, "closedPromiseCapability", $newPromiseCapability(Promise)); - else if ($getByIdDirectPrivate(stream, "state") === $streamClosed) - $putByIdDirectPrivate(reader, "closedPromiseCapability", { - $promise: Promise.$resolve(), - }); - else { - $assert($getByIdDirectPrivate(stream, "state") === $streamErrored); - $putByIdDirectPrivate(reader, "closedPromiseCapability", { - $promise: $newHandledRejectedPromise($getByIdDirectPrivate(stream, "storedError")), - }); - } -} - -export function privateInitializeReadableStreamDefaultController(this, stream, underlyingSource, size, highWaterMark) { - if (!$isReadableStream(stream)) throw new TypeError("ReadableStreamDefaultController needs a ReadableStream"); - - // readableStreamController is initialized with null value. - if ($getByIdDirectPrivate(stream, "readableStreamController") !== null) - throw new TypeError("ReadableStream already has a controller"); - - $putByIdDirectPrivate(this, "controlledReadableStream", stream); - $putByIdDirectPrivate(this, "underlyingSource", underlyingSource); - $putByIdDirectPrivate(this, "queue", $newQueue()); - $putByIdDirectPrivate(this, "started", -1); - $putByIdDirectPrivate(this, "closeRequested", false); - $putByIdDirectPrivate(this, "pullAgain", false); - $putByIdDirectPrivate(this, "pulling", false); - $putByIdDirectPrivate(this, "strategy", $validateAndNormalizeQueuingStrategy(size, highWaterMark)); - - return this; -} - -export function readableStreamDefaultControllerError(controller, error) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - if ($getByIdDirectPrivate(stream, "state") !== $streamReadable) return; - $putByIdDirectPrivate(controller, "queue", $newQueue()); - - $readableStreamError(stream, error); -} - -export function readableStreamPipeTo(stream, sink) { - $assert($isReadableStream(stream)); - - const reader = new ReadableStreamDefaultReader(stream); - - $getByIdDirectPrivate(reader, "closedPromiseCapability").$promise.$then( - () => {}, - e => { - sink.error(e); - }, - ); - - function doPipe() { - $readableStreamDefaultReaderRead(reader).$then( - function (result) { - if (result.done) { - sink.close(); - return; - } - try { - sink.enqueue(result.value); - } catch (e) { - sink.error("ReadableStream chunk enqueueing in the sink failed"); - return; - } - doPipe(); - }, - function (e) { - sink.error(e); - }, - ); - } - doPipe(); -} - -export function acquireReadableStreamDefaultReader(stream) { - var start = $getByIdDirectPrivate(stream, "start"); - if (start) { - start.$call(stream); - } - - return new ReadableStreamDefaultReader(stream); -} - -// https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller, starting from step 6. -// The other part is implemented in privateInitializeReadableStreamDefaultController. -export function setupReadableStreamDefaultController( - stream, - underlyingSource, - size, - highWaterMark, - startMethod, - pullMethod, - cancelMethod, -) { - const controller = new ReadableStreamDefaultController( - stream, - underlyingSource, - size, - highWaterMark, - $isReadableStream, - ); - - const pullAlgorithm = () => $promiseInvokeOrNoopMethod(underlyingSource, pullMethod, [controller]); - const cancelAlgorithm = reason => $promiseInvokeOrNoopMethod(underlyingSource, cancelMethod, [reason]); - - $putByIdDirectPrivate(controller, "pullAlgorithm", pullAlgorithm); - $putByIdDirectPrivate(controller, "cancelAlgorithm", cancelAlgorithm); - $putByIdDirectPrivate(controller, "pull", $readableStreamDefaultControllerPull); - $putByIdDirectPrivate(controller, "cancel", $readableStreamDefaultControllerCancel); - $putByIdDirectPrivate(stream, "readableStreamController", controller); - - $readableStreamDefaultControllerStart(controller); -} - -export function createReadableStreamController(stream, underlyingSource, strategy) { - const type = underlyingSource.type; - const typeString = $toString(type); - - if (typeString === "bytes") { - // if (!$readableByteStreamAPIEnabled()) - // $throwTypeError("ReadableByteStreamController is not implemented"); - - if (strategy.highWaterMark === undefined) strategy.highWaterMark = 0; - if (strategy.size !== undefined) $throwRangeError("Strategy for a ReadableByteStreamController cannot have a size"); - - $putByIdDirectPrivate( - stream, - "readableStreamController", - new ReadableByteStreamController(stream, underlyingSource, strategy.highWaterMark, $isReadableStream), - ); - } else if (typeString === "direct") { - var highWaterMark = strategy?.highWaterMark; - $initializeArrayBufferStream.$call(stream, underlyingSource, highWaterMark); - } else if (type === undefined) { - if (strategy.highWaterMark === undefined) strategy.highWaterMark = 1; - - $setupReadableStreamDefaultController( - stream, - underlyingSource, - strategy.size, - strategy.highWaterMark, - underlyingSource.start, - underlyingSource.pull, - underlyingSource.cancel, - ); - } else throw new RangeError("Invalid type for underlying source"); -} - -export function readableStreamDefaultControllerStart(controller) { - if ($getByIdDirectPrivate(controller, "started") !== -1) return; - - const underlyingSource = $getByIdDirectPrivate(controller, "underlyingSource"); - const startMethod = underlyingSource.start; - $putByIdDirectPrivate(controller, "started", 0); - - $promiseInvokeOrNoopMethodNoCatch(underlyingSource, startMethod, [controller]).$then( - () => { - $putByIdDirectPrivate(controller, "started", 1); - $assert(!$getByIdDirectPrivate(controller, "pulling")); - $assert(!$getByIdDirectPrivate(controller, "pullAgain")); - $readableStreamDefaultControllerCallPullIfNeeded(controller); - }, - error => { - $readableStreamDefaultControllerError(controller, error); - }, - ); -} - -// FIXME: Replace readableStreamPipeTo by below function. -// This method implements the latest https://streams.spec.whatwg.org/#readable-stream-pipe-to. -export function readableStreamPipeToWritableStream( - source, - destination, - preventClose, - preventAbort, - preventCancel, - signal, -) { - // const isDirectStream = !!$getByIdDirectPrivate(source, "start"); - - $assert($isReadableStream(source)); - $assert($isWritableStream(destination)); - $assert(!$isReadableStreamLocked(source)); - $assert(!$isWritableStreamLocked(destination)); - $assert(signal === undefined || $isAbortSignal(signal)); - - if ($getByIdDirectPrivate(source, "underlyingByteSource") !== undefined) - return Promise.$reject("Piping to a readable bytestream is not supported"); - - let pipeState: any = { - source: source, - destination: destination, - preventAbort: preventAbort, - preventCancel: preventCancel, - preventClose: preventClose, - signal: signal, - }; - - pipeState.reader = $acquireReadableStreamDefaultReader(source); - pipeState.writer = $acquireWritableStreamDefaultWriter(destination); - - $putByIdDirectPrivate(source, "disturbed", true); - - pipeState.finalized = false; - pipeState.shuttingDown = false; - pipeState.promiseCapability = $newPromiseCapability(Promise); - pipeState.pendingReadPromiseCapability = $newPromiseCapability(Promise); - pipeState.pendingReadPromiseCapability.$resolve.$call(); - pipeState.pendingWritePromise = Promise.$resolve(); - - if (signal !== undefined) { - const algorithm = reason => { - if (pipeState.finalized) return; - - $pipeToShutdownWithAction( - pipeState, - () => { - const shouldAbortDestination = - !pipeState.preventAbort && $getByIdDirectPrivate(pipeState.destination, "state") === "writable"; - const promiseDestination = shouldAbortDestination - ? $writableStreamAbort(pipeState.destination, reason) - : Promise.$resolve(); - - const shouldAbortSource = - !pipeState.preventCancel && $getByIdDirectPrivate(pipeState.source, "state") === $streamReadable; - const promiseSource = shouldAbortSource - ? $readableStreamCancel(pipeState.source, reason) - : Promise.$resolve(); - - let promiseCapability = $newPromiseCapability(Promise); - let shouldWait = true; - let handleResolvedPromise = () => { - if (shouldWait) { - shouldWait = false; - return; - } - promiseCapability.$resolve.$call(); - }; - let handleRejectedPromise = e => { - promiseCapability.$reject.$call(undefined, e); - }; - promiseDestination.$then(handleResolvedPromise, handleRejectedPromise); - promiseSource.$then(handleResolvedPromise, handleRejectedPromise); - return promiseCapability.$promise; - }, - reason, - ); - }; - if ($whenSignalAborted(signal, algorithm)) return pipeState.promiseCapability.$promise; - } - - $pipeToErrorsMustBePropagatedForward(pipeState); - $pipeToErrorsMustBePropagatedBackward(pipeState); - $pipeToClosingMustBePropagatedForward(pipeState); - $pipeToClosingMustBePropagatedBackward(pipeState); - - $pipeToLoop(pipeState); - - return pipeState.promiseCapability.$promise; -} - -export function pipeToLoop(pipeState) { - if (pipeState.shuttingDown) return; - - $pipeToDoReadWrite(pipeState).$then(result => { - if (result) $pipeToLoop(pipeState); - }); -} - -export function pipeToDoReadWrite(pipeState) { - $assert(!pipeState.shuttingDown); - - pipeState.pendingReadPromiseCapability = $newPromiseCapability(Promise); - $getByIdDirectPrivate(pipeState.writer, "readyPromise").$promise.$then( - () => { - if (pipeState.shuttingDown) { - pipeState.pendingReadPromiseCapability.$resolve.$call(undefined, false); - return; - } - - $readableStreamDefaultReaderRead(pipeState.reader).$then( - result => { - const canWrite = !result.done && $getByIdDirectPrivate(pipeState.writer, "stream") !== undefined; - pipeState.pendingReadPromiseCapability.$resolve.$call(undefined, canWrite); - if (!canWrite) return; - - pipeState.pendingWritePromise = $writableStreamDefaultWriterWrite(pipeState.writer, result.value); - }, - e => { - pipeState.pendingReadPromiseCapability.$resolve.$call(undefined, false); - }, - ); - }, - e => { - pipeState.pendingReadPromiseCapability.$resolve.$call(undefined, false); - }, - ); - return pipeState.pendingReadPromiseCapability.$promise; -} - -export function pipeToErrorsMustBePropagatedForward(pipeState) { - const action = () => { - pipeState.pendingReadPromiseCapability.$resolve.$call(undefined, false); - const error = $getByIdDirectPrivate(pipeState.source, "storedError"); - if (!pipeState.preventAbort) { - $pipeToShutdownWithAction(pipeState, () => $writableStreamAbort(pipeState.destination, error), error); - return; - } - $pipeToShutdown(pipeState, error); - }; - - if ($getByIdDirectPrivate(pipeState.source, "state") === $streamErrored) { - action(); - return; - } - - $getByIdDirectPrivate(pipeState.reader, "closedPromiseCapability").$promise.$then(undefined, action); -} - -export function pipeToErrorsMustBePropagatedBackward(pipeState) { - const action = () => { - const error = $getByIdDirectPrivate(pipeState.destination, "storedError"); - if (!pipeState.preventCancel) { - $pipeToShutdownWithAction(pipeState, () => $readableStreamCancel(pipeState.source, error), error); - return; - } - $pipeToShutdown(pipeState, error); - }; - if ($getByIdDirectPrivate(pipeState.destination, "state") === "errored") { - action(); - return; - } - $getByIdDirectPrivate(pipeState.writer, "closedPromise").$promise.$then(undefined, action); -} - -export function pipeToClosingMustBePropagatedForward(pipeState) { - const action = () => { - pipeState.pendingReadPromiseCapability.$resolve.$call(undefined, false); - // const error = $getByIdDirectPrivate(pipeState.source, "storedError"); - if (!pipeState.preventClose) { - $pipeToShutdownWithAction(pipeState, () => - $writableStreamDefaultWriterCloseWithErrorPropagation(pipeState.writer), - ); - return; - } - $pipeToShutdown(pipeState); - }; - if ($getByIdDirectPrivate(pipeState.source, "state") === $streamClosed) { - action(); - return; - } - $getByIdDirectPrivate(pipeState.reader, "closedPromiseCapability").$promise.$then(action, undefined); -} - -export function pipeToClosingMustBePropagatedBackward(pipeState) { - if ( - !$writableStreamCloseQueuedOrInFlight(pipeState.destination) && - $getByIdDirectPrivate(pipeState.destination, "state") !== "closed" - ) - return; - - // $assert no chunks have been read/written - - const error = new TypeError("closing is propagated backward"); - if (!pipeState.preventCancel) { - $pipeToShutdownWithAction(pipeState, () => $readableStreamCancel(pipeState.source, error), error); - return; - } - $pipeToShutdown(pipeState, error); -} - -export function pipeToShutdownWithAction(pipeState, action) { - if (pipeState.shuttingDown) return; - - pipeState.shuttingDown = true; - - const hasError = arguments.length > 2; - const error = arguments[2]; - const finalize = () => { - const promise = action(); - promise.$then( - () => { - if (hasError) $pipeToFinalize(pipeState, error); - else $pipeToFinalize(pipeState); - }, - e => { - $pipeToFinalize(pipeState, e); - }, - ); - }; - - if ( - $getByIdDirectPrivate(pipeState.destination, "state") === "writable" && - !$writableStreamCloseQueuedOrInFlight(pipeState.destination) - ) { - pipeState.pendingReadPromiseCapability.$promise.$then( - () => { - pipeState.pendingWritePromise.$then(finalize, finalize); - }, - e => $pipeToFinalize(pipeState, e), - ); - return; - } - - finalize(); -} - -export function pipeToShutdown(pipeState) { - if (pipeState.shuttingDown) return; - - pipeState.shuttingDown = true; - - const hasError = arguments.length > 1; - const error = arguments[1]; - const finalize = () => { - if (hasError) $pipeToFinalize(pipeState, error); - else $pipeToFinalize(pipeState); - }; - - if ( - $getByIdDirectPrivate(pipeState.destination, "state") === "writable" && - !$writableStreamCloseQueuedOrInFlight(pipeState.destination) - ) { - pipeState.pendingReadPromiseCapability.$promise.$then( - () => { - pipeState.pendingWritePromise.$then(finalize, finalize); - }, - e => $pipeToFinalize(pipeState, e), - ); - return; - } - finalize(); -} - -export function pipeToFinalize(pipeState) { - $writableStreamDefaultWriterRelease(pipeState.writer); - $readableStreamReaderGenericRelease(pipeState.reader); - - // Instead of removing the abort algorithm as per spec, we make it a no-op which is equivalent. - pipeState.finalized = true; - - if (arguments.length > 1) pipeState.promiseCapability.$reject.$call(undefined, arguments[1]); - else pipeState.promiseCapability.$resolve.$call(); -} - -export function readableStreamTee(stream, shouldClone) { - $assert($isReadableStream(stream)); - $assert(typeof shouldClone === "boolean"); - - var start_ = $getByIdDirectPrivate(stream, "start"); - if (start_) { - $putByIdDirectPrivate(stream, "start", undefined); - start_(); - } - - const reader = new $ReadableStreamDefaultReader(stream); - - const teeState = { - closedOrErrored: false, - canceled1: false, - canceled2: false, - reason1: undefined, - reason2: undefined, - }; - - teeState.cancelPromiseCapability = $newPromiseCapability(Promise); - - const pullFunction = $readableStreamTeePullFunction(teeState, reader, shouldClone); - - const branch1Source = {}; - $putByIdDirectPrivate(branch1Source, "pull", pullFunction); - $putByIdDirectPrivate(branch1Source, "cancel", $readableStreamTeeBranch1CancelFunction(teeState, stream)); - - const branch2Source = {}; - $putByIdDirectPrivate(branch2Source, "pull", pullFunction); - $putByIdDirectPrivate(branch2Source, "cancel", $readableStreamTeeBranch2CancelFunction(teeState, stream)); - - const branch1 = new $ReadableStream(branch1Source); - const branch2 = new $ReadableStream(branch2Source); - - $getByIdDirectPrivate(reader, "closedPromiseCapability").$promise.$then(undefined, function (e) { - if (teeState.closedOrErrored) return; - $readableStreamDefaultControllerError(branch1.$readableStreamController, e); - $readableStreamDefaultControllerError(branch2.$readableStreamController, e); - teeState.closedOrErrored = true; - if (!teeState.canceled1 || !teeState.canceled2) teeState.cancelPromiseCapability.$resolve.$call(); - }); - - // Additional fields compared to the spec, as they are needed within pull/cancel functions. - teeState.branch1 = branch1; - teeState.branch2 = branch2; - - return [branch1, branch2]; -} - -export function readableStreamTeePullFunction(teeState, reader, shouldClone) { - return function () { - Promise.prototype.$then.$call($readableStreamDefaultReaderRead(reader), function (result) { - $assert($isObject(result)); - $assert(typeof result.done === "boolean"); - if (result.done && !teeState.closedOrErrored) { - if (!teeState.canceled1) $readableStreamDefaultControllerClose(teeState.branch1.$readableStreamController); - if (!teeState.canceled2) $readableStreamDefaultControllerClose(teeState.branch2.$readableStreamController); - teeState.closedOrErrored = true; - if (!teeState.canceled1 || !teeState.canceled2) teeState.cancelPromiseCapability.$resolve.$call(); - } - if (teeState.closedOrErrored) return; - if (!teeState.canceled1) - $readableStreamDefaultControllerEnqueue(teeState.branch1.$readableStreamController, result.value); - if (!teeState.canceled2) - $readableStreamDefaultControllerEnqueue( - teeState.branch2.$readableStreamController, - shouldClone ? $structuredCloneForStream(result.value) : result.value, - ); - }); - }; -} - -export function readableStreamTeeBranch1CancelFunction(teeState, stream) { - return function (r) { - teeState.canceled1 = true; - teeState.reason1 = r; - if (teeState.canceled2) { - $readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).$then( - teeState.cancelPromiseCapability.$resolve, - teeState.cancelPromiseCapability.$reject, - ); - } - return teeState.cancelPromiseCapability.$promise; - }; -} - -export function readableStreamTeeBranch2CancelFunction(teeState, stream) { - return function (r) { - teeState.canceled2 = true; - teeState.reason2 = r; - if (teeState.canceled1) { - $readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).$then( - teeState.cancelPromiseCapability.$resolve, - teeState.cancelPromiseCapability.$reject, - ); - } - return teeState.cancelPromiseCapability.$promise; - }; -} - -export function isReadableStream(stream) { - // Spec tells to return true only if stream has a readableStreamController internal slot. - // However, since it is a private slot, it cannot be checked using hasOwnProperty(). - // Therefore, readableStreamController is initialized with null value. - return $isObject(stream) && $getByIdDirectPrivate(stream, "readableStreamController") !== undefined; -} - -export function isReadableStreamDefaultReader(reader) { - // Spec tells to return true only if reader has a readRequests internal slot. - // However, since it is a private slot, it cannot be checked using hasOwnProperty(). - // Since readRequests is initialized with an empty array, the following test is ok. - return $isObject(reader) && !!$getByIdDirectPrivate(reader, "readRequests"); -} - -export function isReadableStreamDefaultController(controller) { - // Spec tells to return true only if controller has an underlyingSource internal slot. - // However, since it is a private slot, it cannot be checked using hasOwnProperty(). - // underlyingSource is obtained in ReadableStream constructor: if undefined, it is set - // to an empty object. Therefore, following test is ok. - return $isObject(controller) && !!$getByIdDirectPrivate(controller, "underlyingSource"); -} - -export function readDirectStream(stream, sink, underlyingSource) { - $putByIdDirectPrivate(stream, "underlyingSource", undefined); - $putByIdDirectPrivate(stream, "start", undefined); - - function close(stream, reason) { - if (reason && underlyingSource?.cancel) { - try { - var prom = underlyingSource.cancel(reason); - $markPromiseAsHandled(prom); - } catch (e) {} - - underlyingSource = undefined; - } - - if (stream) { - $putByIdDirectPrivate(stream, "readableStreamController", undefined); - $putByIdDirectPrivate(stream, "reader", undefined); - if (reason) { - $putByIdDirectPrivate(stream, "state", $streamErrored); - $putByIdDirectPrivate(stream, "storedError", reason); - } else { - $putByIdDirectPrivate(stream, "state", $streamClosed); - } - stream = undefined; - } - } - - if (!underlyingSource.pull) { - close(); - return; - } - - if (!$isCallable(underlyingSource.pull)) { - close(); - $throwTypeError("pull is not a function"); - return; - } - - $putByIdDirectPrivate(stream, "readableStreamController", sink); - const highWaterMark = $getByIdDirectPrivate(stream, "highWaterMark"); - - sink.start({ - highWaterMark: !highWaterMark || highWaterMark < 64 ? 64 : highWaterMark, - }); - - $startDirectStream.$call(sink, stream, underlyingSource.pull, close); - $putByIdDirectPrivate(stream, "reader", {}); - - var maybePromise = underlyingSource.pull(sink); - sink = undefined; - if (maybePromise && $isPromise(maybePromise)) { - return maybePromise.$then(() => {}); - } -} - -$linkTimeConstant; -export function assignToStream(stream, sink) { - // The stream is either a direct stream or a "default" JS stream - var underlyingSource = $getByIdDirectPrivate(stream, "underlyingSource"); - - // we know it's a direct stream when $underlyingSource is set - if (underlyingSource) { - try { - return $readDirectStream(stream, sink, underlyingSource); - } catch (e) { - throw e; - } finally { - underlyingSource = undefined; - stream = undefined; - sink = undefined; - } - } - - return $readStreamIntoSink(stream, sink, true); -} - -export async function readStreamIntoSink(stream, sink, isNative) { - var didClose = false; - var didThrow = false; - try { - var reader = stream.getReader(); - var many = reader.readMany(); - if (many && $isPromise(many)) { - many = await many; - } - if (many.done) { - didClose = true; - return sink.end(); - } - var wroteCount = many.value.length; - const highWaterMark = $getByIdDirectPrivate(stream, "highWaterMark"); - if (isNative) - $startDirectStream.$call(sink, stream, undefined, () => !didThrow && $markPromiseAsHandled(stream.cancel())); - - sink.start({ highWaterMark: highWaterMark || 0 }); - - for (var i = 0, values = many.value, length = many.value.length; i < length; i++) { - sink.write(values[i]); - } - - var streamState = $getByIdDirectPrivate(stream, "state"); - if (streamState === $streamClosed) { - didClose = true; - return sink.end(); - } - - while (true) { - var { value, done } = await reader.read(); - if (done) { - didClose = true; - return sink.end(); - } - - sink.write(value); - } - } catch (e) { - didThrow = true; - - try { - reader = undefined; - const prom = stream.cancel(e); - $markPromiseAsHandled(prom); - } catch (j) {} - - if (sink && !didClose) { - didClose = true; - try { - sink.close(e); - } catch (j) { - throw new globalThis.AggregateError([e, j]); - } - } - - throw e; - } finally { - if (reader) { - try { - reader.releaseLock(); - } catch (e) {} - reader = undefined; - } - sink = undefined; - var streamState = $getByIdDirectPrivate(stream, "state"); - if (stream) { - // make it easy for this to be GC'd - // but don't do property transitions - var readableStreamController = $getByIdDirectPrivate(stream, "readableStreamController"); - if (readableStreamController) { - if ($getByIdDirectPrivate(readableStreamController, "underlyingSource")) - $putByIdDirectPrivate(readableStreamController, "underlyingSource", undefined); - if ($getByIdDirectPrivate(readableStreamController, "controlledReadableStream")) - $putByIdDirectPrivate(readableStreamController, "controlledReadableStream", undefined); - - $putByIdDirectPrivate(stream, "readableStreamController", null); - if ($getByIdDirectPrivate(stream, "underlyingSource")) - $putByIdDirectPrivate(stream, "underlyingSource", undefined); - readableStreamController = undefined; - } - - if (!didThrow && streamState !== $streamClosed && streamState !== $streamErrored) { - $readableStreamClose(stream); - } - stream = undefined; - } - } -} - -export function handleDirectStreamError(e) { - var controller = this; - var sink = controller.$sink; - if (sink) { - $putByIdDirectPrivate(controller, "sink", undefined); - try { - sink.close(e); - } catch (f) {} - } - - this.error = this.flush = this.write = this.close = this.end = $onReadableStreamDirectControllerClosed; - - if (typeof this.$underlyingSource.close === "function") { - try { - this.$underlyingSource.close.$call(this.$underlyingSource, e); - } catch (e) {} - } - - try { - var pend = controller._pendingRead; - if (pend) { - controller._pendingRead = undefined; - $rejectPromise(pend, e); - } - } catch (f) {} - var stream = controller.$controlledReadableStream; - if (stream) $readableStreamError(stream, e); -} - -export function handleDirectStreamErrorReject(e) { - $handleDirectStreamError.$call(this, e); - return Promise.$reject(e); -} - -export function onPullDirectStream(controller) { - var stream = controller.$controlledReadableStream; - if (!stream || $getByIdDirectPrivate(stream, "state") !== $streamReadable) return; - - // pull is in progress - // this is a recursive call - // ignore it - if (controller._deferClose === -1) { - return; - } - - controller._deferClose = -1; - controller._deferFlush = -1; - var deferClose; - var deferFlush; - - // Direct streams allow $pull to be called multiple times, unlike the spec. - // Backpressure is handled by the destination, not by the underlying source. - // In this case, we rely on the heuristic that repeatedly draining in the same tick - // is bad for performance - // this code is only run when consuming a direct stream from JS - // without the HTTP server or anything else - try { - var result = controller.$underlyingSource.pull(controller); - - if (result && $isPromise(result)) { - if (controller._handleError === undefined) { - controller._handleError = $handleDirectStreamErrorReject.bind(controller); - } - - Promise.prototype.catch.$call(result, controller._handleError); - } - } catch (e) { - return $handleDirectStreamErrorReject.$call(controller, e); - } finally { - deferClose = controller._deferClose; - deferFlush = controller._deferFlush; - controller._deferFlush = controller._deferClose = 0; - } - - var promiseToReturn; - - if (controller._pendingRead === undefined) { - controller._pendingRead = promiseToReturn = $newPromise(); - } else { - promiseToReturn = $readableStreamAddReadRequest(stream); - } - - // they called close during $pull() - // we delay that - if (deferClose === 1) { - var reason = controller._deferCloseReason; - controller._deferCloseReason = undefined; - $onCloseDirectStream.$call(controller, reason); - return promiseToReturn; - } - - // not done, but they called flush() - if (deferFlush === 1) { - $onFlushDirectStream.$call(controller); - } - - return promiseToReturn; -} - -export function noopDoneFunction() { - return Promise.$resolve({ value: undefined, done: true }); -} - -export function onReadableStreamDirectControllerClosed(reason) { - $throwTypeError("ReadableStreamDirectController is now closed"); -} - -export function onCloseDirectStream(reason) { - var stream = this.$controlledReadableStream; - if (!stream || $getByIdDirectPrivate(stream, "state") !== $streamReadable) return; - - if (this._deferClose !== 0) { - this._deferClose = 1; - this._deferCloseReason = reason; - return; - } - - $putByIdDirectPrivate(stream, "state", $streamClosing); - if (typeof this.$underlyingSource.close === "function") { - try { - this.$underlyingSource.close.$call(this.$underlyingSource, reason); - } catch (e) {} - } - - var flushed; - try { - flushed = this.$sink.end(); - $putByIdDirectPrivate(this, "sink", undefined); - } catch (e) { - if (this._pendingRead) { - var read = this._pendingRead; - this._pendingRead = undefined; - $rejectPromise(read, e); - } - $readableStreamError(stream, e); - return; - } - - this.error = this.flush = this.write = this.close = this.end = $onReadableStreamDirectControllerClosed; - - var reader = $getByIdDirectPrivate(stream, "reader"); - - if (reader && $isReadableStreamDefaultReader(reader)) { - var _pendingRead = this._pendingRead; - if (_pendingRead && $isPromise(_pendingRead) && flushed?.byteLength) { - this._pendingRead = undefined; - $fulfillPromise(_pendingRead, { value: flushed, done: false }); - $readableStreamClose(stream); - return; - } - } - - if (flushed?.byteLength) { - var requests = $getByIdDirectPrivate(reader, "readRequests"); - if (requests?.isNotEmpty()) { - $readableStreamFulfillReadRequest(stream, flushed, false); - $readableStreamClose(stream); - return; - } - - $putByIdDirectPrivate(stream, "state", $streamReadable); - this.$pull = () => { - var thisResult = $createFulfilledPromise({ - value: flushed, - done: false, - }); - flushed = undefined; - $readableStreamClose(stream); - stream = undefined; - return thisResult; - }; - } else if (this._pendingRead) { - var read = this._pendingRead; - this._pendingRead = undefined; - $putByIdDirectPrivate(this, "pull", $noopDoneFunction); - $fulfillPromise(read, { value: undefined, done: true }); - } - - $readableStreamClose(stream); -} - -export function onFlushDirectStream() { - var stream = this.$controlledReadableStream; - var reader = $getByIdDirectPrivate(stream, "reader"); - if (!reader || !$isReadableStreamDefaultReader(reader)) { - return; - } - - var _pendingRead = this._pendingRead; - this._pendingRead = undefined; - if (_pendingRead && $isPromise(_pendingRead)) { - var flushed = this.$sink.flush(); - if (flushed?.byteLength) { - this._pendingRead = $getByIdDirectPrivate(stream, "readRequests")?.shift(); - $fulfillPromise(_pendingRead, { value: flushed, done: false }); - } else { - this._pendingRead = _pendingRead; - } - } else if ($getByIdDirectPrivate(stream, "readRequests")?.isNotEmpty()) { - var flushed = this.$sink.flush(); - if (flushed?.byteLength) { - $readableStreamFulfillReadRequest(stream, flushed, false); - } - } else if (this._deferFlush === -1) { - this._deferFlush = 1; - } -} - -export function createTextStream(highWaterMark) { - var sink; - var array = []; - var hasString = false; - var hasBuffer = false; - var rope = ""; - var estimatedLength = $toLength(0); - var capability = $newPromiseCapability(Promise); - var calledDone = false; - - sink = { - start() {}, - write(chunk) { - if (typeof chunk === "string") { - var chunkLength = $toLength(chunk.length); - if (chunkLength > 0) { - rope += chunk; - hasString = true; - // TODO: utf16 byte length - estimatedLength += chunkLength; - } - - return chunkLength; - } - - if (!chunk || !($ArrayBuffer.$isView(chunk) || chunk instanceof $ArrayBuffer)) { - $throwTypeError("Expected text, ArrayBuffer or ArrayBufferView"); - } - - const byteLength = $toLength(chunk.byteLength); - if (byteLength > 0) { - hasBuffer = true; - if (rope.length > 0) { - $arrayPush(array, rope, chunk); - rope = ""; - } else { - $arrayPush(array, chunk); - } - } - estimatedLength += byteLength; - return byteLength; - }, - - flush() { - return 0; - }, - - end() { - if (calledDone) { - return ""; - } - return sink.fulfill(); - }, - - fulfill() { - calledDone = true; - const result = sink.finishInternal(); - - $fulfillPromise(capability.$promise, result); - return result; - }, - - finishInternal() { - if (!hasString && !hasBuffer) { - return ""; - } - - if (hasString && !hasBuffer) { - return rope; - } - - if (hasBuffer && !hasString) { - return new globalThis.TextDecoder().decode($Bun.concatArrayBuffers(array)); - } - - // worst case: mixed content - - var arrayBufferSink = new $Bun.ArrayBufferSink(); - arrayBufferSink.start({ - highWaterMark: estimatedLength, - asUint8Array: true, - }); - for (let item of array) { - arrayBufferSink.write(item); - } - array.length = 0; - if (rope.length > 0) { - arrayBufferSink.write(rope); - rope = ""; - } - - // TODO: use builtin - return new globalThis.TextDecoder().decode(arrayBufferSink.end()); - }, - - close() { - try { - if (!calledDone) { - calledDone = true; - sink.fulfill(); - } - } catch (e) {} - }, - }; - - return [sink, capability]; -} - -export function initializeTextStream(underlyingSource, highWaterMark) { - var [sink, closingPromise] = $createTextStream(highWaterMark); - - var controller = { - $underlyingSource: underlyingSource, - $pull: $onPullDirectStream, - $controlledReadableStream: this, - $sink: sink, - close: $onCloseDirectStream, - write: sink.write, - error: $handleDirectStreamError, - end: $onCloseDirectStream, - $close: $onCloseDirectStream, - flush: $onFlushDirectStream, - _pendingRead: undefined, - _deferClose: 0, - _deferFlush: 0, - _deferCloseReason: undefined, - _handleError: undefined, - }; - - $putByIdDirectPrivate(this, "readableStreamController", controller); - $putByIdDirectPrivate(this, "underlyingSource", undefined); - $putByIdDirectPrivate(this, "start", undefined); - return closingPromise; -} - -export function initializeArrayStream(underlyingSource, highWaterMark) { - var array = []; - var closingPromise = $newPromiseCapability(Promise); - var calledDone = false; - - function fulfill() { - calledDone = true; - closingPromise.$resolve.$call(undefined, array); - return array; - } - - var sink = { - start() {}, - write(chunk) { - $arrayPush(array, chunk); - return chunk.byteLength || chunk.length; - }, - - flush() { - return 0; - }, - - end() { - if (calledDone) { - return []; - } - return fulfill(); - }, - - close() { - if (!calledDone) { - fulfill(); - } - }, - }; - - var controller = { - $underlyingSource: underlyingSource, - $pull: $onPullDirectStream, - $controlledReadableStream: this, - $sink: sink, - close: $onCloseDirectStream, - write: sink.write, - error: $handleDirectStreamError, - end: $onCloseDirectStream, - $close: $onCloseDirectStream, - flush: $onFlushDirectStream, - _pendingRead: undefined, - _deferClose: 0, - _deferFlush: 0, - _deferCloseReason: undefined, - _handleError: undefined, - }; - - $putByIdDirectPrivate(this, "readableStreamController", controller); - $putByIdDirectPrivate(this, "underlyingSource", undefined); - $putByIdDirectPrivate(this, "start", undefined); - return closingPromise; -} - -export function initializeArrayBufferStream(underlyingSource, highWaterMark) { - // This is the fallback implementation for direct streams - // When we don't know what the destination type is - // We assume it is a Uint8Array. - - var opts = - highWaterMark && typeof highWaterMark === "number" - ? { highWaterMark, stream: true, asUint8Array: true } - : { stream: true, asUint8Array: true }; - var sink = new $Bun.ArrayBufferSink(); - sink.start(opts); - - var controller = { - $underlyingSource: underlyingSource, - $pull: $onPullDirectStream, - $controlledReadableStream: this, - $sink: sink, - close: $onCloseDirectStream, - write: sink.write.bind(sink), - error: $handleDirectStreamError, - end: $onCloseDirectStream, - $close: $onCloseDirectStream, - flush: $onFlushDirectStream, - _pendingRead: undefined, - _deferClose: 0, - _deferFlush: 0, - _deferCloseReason: undefined, - _handleError: undefined, - }; - - $putByIdDirectPrivate(this, "readableStreamController", controller); - $putByIdDirectPrivate(this, "underlyingSource", undefined); - $putByIdDirectPrivate(this, "start", undefined); -} - -export function readableStreamError(stream, error) { - $assert($isReadableStream(stream)); - $assert($getByIdDirectPrivate(stream, "state") === $streamReadable); - $putByIdDirectPrivate(stream, "state", $streamErrored); - $putByIdDirectPrivate(stream, "storedError", error); - - const reader = $getByIdDirectPrivate(stream, "reader"); - - if (!reader) return; - - if ($isReadableStreamDefaultReader(reader)) { - const requests = $getByIdDirectPrivate(reader, "readRequests"); - $putByIdDirectPrivate(reader, "readRequests", $createFIFO()); - for (var request = requests.shift(); request; request = requests.shift()) $rejectPromise(request, error); - } else { - $assert($isReadableStreamBYOBReader(reader)); - const requests = $getByIdDirectPrivate(reader, "readIntoRequests"); - $putByIdDirectPrivate(reader, "readIntoRequests", $createFIFO()); - for (var request = requests.shift(); request; request = requests.shift()) $rejectPromise(request, error); - } - - $getByIdDirectPrivate(reader, "closedPromiseCapability").$reject.$call(undefined, error); - const promise = $getByIdDirectPrivate(reader, "closedPromiseCapability").$promise; - $markPromiseAsHandled(promise); -} - -export function readableStreamDefaultControllerShouldCallPull(controller) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - - if (!$readableStreamDefaultControllerCanCloseOrEnqueue(controller)) return false; - if (!($getByIdDirectPrivate(controller, "started") === 1)) return false; - if ( - (!$isReadableStreamLocked(stream) || - !$getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests")?.isNotEmpty()) && - $readableStreamDefaultControllerGetDesiredSize(controller) <= 0 - ) - return false; - const desiredSize = $readableStreamDefaultControllerGetDesiredSize(controller); - $assert(desiredSize !== null); - return desiredSize > 0; -} - -export function readableStreamDefaultControllerCallPullIfNeeded(controller) { - // FIXME: use $readableStreamDefaultControllerShouldCallPull - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - - if (!$readableStreamDefaultControllerCanCloseOrEnqueue(controller)) return; - if (!($getByIdDirectPrivate(controller, "started") === 1)) return; - if ( - (!$isReadableStreamLocked(stream) || - !$getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests")?.isNotEmpty()) && - $readableStreamDefaultControllerGetDesiredSize(controller) <= 0 - ) - return; - - if ($getByIdDirectPrivate(controller, "pulling")) { - $putByIdDirectPrivate(controller, "pullAgain", true); - return; - } - - $assert(!$getByIdDirectPrivate(controller, "pullAgain")); - $putByIdDirectPrivate(controller, "pulling", true); - - $getByIdDirectPrivate(controller, "pullAlgorithm") - .$call(undefined) - .$then( - function () { - $putByIdDirectPrivate(controller, "pulling", false); - if ($getByIdDirectPrivate(controller, "pullAgain")) { - $putByIdDirectPrivate(controller, "pullAgain", false); - - $readableStreamDefaultControllerCallPullIfNeeded(controller); - } - }, - function (error) { - $readableStreamDefaultControllerError(controller, error); - }, - ); -} - -export function isReadableStreamLocked(stream) { - $assert($isReadableStream(stream)); - return !!$getByIdDirectPrivate(stream, "reader"); -} - -export function readableStreamDefaultControllerGetDesiredSize(controller) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - const state = $getByIdDirectPrivate(stream, "state"); - - if (state === $streamErrored) return null; - if (state === $streamClosed) return 0; - - return $getByIdDirectPrivate(controller, "strategy").highWaterMark - $getByIdDirectPrivate(controller, "queue").size; -} - -export function readableStreamReaderGenericCancel(reader, reason) { - const stream = $getByIdDirectPrivate(reader, "ownerReadableStream"); - $assert(!!stream); - return $readableStreamCancel(stream, reason); -} - -export function readableStreamCancel(stream, reason) { - $putByIdDirectPrivate(stream, "disturbed", true); - const state = $getByIdDirectPrivate(stream, "state"); - if (state === $streamClosed) return Promise.$resolve(); - if (state === $streamErrored) return Promise.$reject($getByIdDirectPrivate(stream, "storedError")); - $readableStreamClose(stream); - - var controller = $getByIdDirectPrivate(stream, "readableStreamController"); - var cancel = controller.$cancel; - if (cancel) { - return cancel(controller, reason).$then(function () {}); - } - - var close = controller.close; - if (close) { - return Promise.$resolve(controller.close(reason)); - } - - $throwTypeError("ReadableStreamController has no cancel or close method"); -} - -export function readableStreamDefaultControllerCancel(controller, reason) { - $putByIdDirectPrivate(controller, "queue", $newQueue()); - return $getByIdDirectPrivate(controller, "cancelAlgorithm").$call(undefined, reason); -} - -export function readableStreamDefaultControllerPull(controller) { - var queue = $getByIdDirectPrivate(controller, "queue"); - if (queue.content.isNotEmpty()) { - const chunk = $dequeueValue(queue); - if ($getByIdDirectPrivate(controller, "closeRequested") && queue.content.isEmpty()) - $readableStreamClose($getByIdDirectPrivate(controller, "controlledReadableStream")); - else $readableStreamDefaultControllerCallPullIfNeeded(controller); - - return $createFulfilledPromise({ value: chunk, done: false }); - } - const pendingPromise = $readableStreamAddReadRequest($getByIdDirectPrivate(controller, "controlledReadableStream")); - $readableStreamDefaultControllerCallPullIfNeeded(controller); - return pendingPromise; -} - -export function readableStreamDefaultControllerClose(controller) { - $assert($readableStreamDefaultControllerCanCloseOrEnqueue(controller)); - $putByIdDirectPrivate(controller, "closeRequested", true); - if ($getByIdDirectPrivate(controller, "queue")?.content?.isEmpty()) - $readableStreamClose($getByIdDirectPrivate(controller, "controlledReadableStream")); -} - -export function readableStreamClose(stream) { - $assert($getByIdDirectPrivate(stream, "state") === $streamReadable); - $putByIdDirectPrivate(stream, "state", $streamClosed); - if (!$getByIdDirectPrivate(stream, "reader")) return; - - if ($isReadableStreamDefaultReader($getByIdDirectPrivate(stream, "reader"))) { - const requests = $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests"); - if (requests.isNotEmpty()) { - $putByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests", $createFIFO()); - - for (var request = requests.shift(); request; request = requests.shift()) - $fulfillPromise(request, { value: undefined, done: true }); - } - } - - $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "closedPromiseCapability").$resolve.$call(); -} - -export function readableStreamFulfillReadRequest(stream, chunk, done) { - const readRequest = $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests").shift(); - $fulfillPromise(readRequest, { value: chunk, done: done }); -} - -export function readableStreamDefaultControllerEnqueue(controller, chunk) { - const stream = $getByIdDirectPrivate(controller, "controlledReadableStream"); - // this is checked by callers - $assert($readableStreamDefaultControllerCanCloseOrEnqueue(controller)); - - if ( - $isReadableStreamLocked(stream) && - $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests")?.isNotEmpty() - ) { - $readableStreamFulfillReadRequest(stream, chunk, false); - $readableStreamDefaultControllerCallPullIfNeeded(controller); - return; - } - - try { - let chunkSize = 1; - if ($getByIdDirectPrivate(controller, "strategy").size !== undefined) - chunkSize = $getByIdDirectPrivate(controller, "strategy").size(chunk); - $enqueueValueWithSize($getByIdDirectPrivate(controller, "queue"), chunk, chunkSize); - } catch (error) { - $readableStreamDefaultControllerError(controller, error); - throw error; - } - $readableStreamDefaultControllerCallPullIfNeeded(controller); -} - -export function readableStreamDefaultReaderRead(reader) { - const stream = $getByIdDirectPrivate(reader, "ownerReadableStream"); - $assert(!!stream); - const state = $getByIdDirectPrivate(stream, "state"); - - $putByIdDirectPrivate(stream, "disturbed", true); - if (state === $streamClosed) return $createFulfilledPromise({ value: undefined, done: true }); - if (state === $streamErrored) return Promise.$reject($getByIdDirectPrivate(stream, "storedError")); - $assert(state === $streamReadable); - - return $getByIdDirectPrivate(stream, "readableStreamController").$pull( - $getByIdDirectPrivate(stream, "readableStreamController"), - ); -} - -export function readableStreamAddReadRequest(stream) { - $assert($isReadableStreamDefaultReader($getByIdDirectPrivate(stream, "reader"))); - $assert($getByIdDirectPrivate(stream, "state") == $streamReadable); - - const readRequest = $newPromise(); - - $getByIdDirectPrivate($getByIdDirectPrivate(stream, "reader"), "readRequests").push(readRequest); - - return readRequest; -} - -export function isReadableStreamDisturbed(stream) { - $assert($isReadableStream(stream)); - return $getByIdDirectPrivate(stream, "disturbed"); -} - -export function readableStreamReaderGenericRelease(reader) { - $assert(!!$getByIdDirectPrivate(reader, "ownerReadableStream")); - $assert($getByIdDirectPrivate($getByIdDirectPrivate(reader, "ownerReadableStream"), "reader") === reader); - - if ($getByIdDirectPrivate($getByIdDirectPrivate(reader, "ownerReadableStream"), "state") === $streamReadable) - $getByIdDirectPrivate(reader, "closedPromiseCapability").$reject.$call( - undefined, - $makeTypeError("releasing lock of reader whose stream is still in readable state"), - ); - else - $putByIdDirectPrivate(reader, "closedPromiseCapability", { - $promise: $newHandledRejectedPromise($makeTypeError("reader released lock")), - }); - - const promise = $getByIdDirectPrivate(reader, "closedPromiseCapability").$promise; - $markPromiseAsHandled(promise); - $putByIdDirectPrivate($getByIdDirectPrivate(reader, "ownerReadableStream"), "reader", undefined); - $putByIdDirectPrivate(reader, "ownerReadableStream", undefined); -} - -export function readableStreamDefaultControllerCanCloseOrEnqueue(controller) { - return ( - !$getByIdDirectPrivate(controller, "closeRequested") && - $getByIdDirectPrivate($getByIdDirectPrivate(controller, "controlledReadableStream"), "state") === $streamReadable - ); -} - -export function lazyLoadStream(stream, autoAllocateChunkSize) { - var nativeType = $getByIdDirectPrivate(stream, "bunNativeType"); - var nativePtr = $getByIdDirectPrivate(stream, "bunNativePtr"); - var Prototype = $lazyStreamPrototypeMap.$get(nativeType); - if (Prototype === undefined) { - var [pull, start, cancel, setClose, deinit, setRefOrUnref, drain] = $lazyLoad(nativeType); - var closer = [false]; - var handleResult; - function handleNativeReadableStreamPromiseResult(val) { - var { c, v } = this; - this.c = undefined; - this.v = undefined; - handleResult(val, c, v); - } - - function callClose(controller) { - try { - controller.close(); - } catch (e) { - globalThis.reportError(e); - } - } - - handleResult = function handleResult(result, controller, view) { - if (result && $isPromise(result)) { - return result.then( - handleNativeReadableStreamPromiseResult.bind({ - c: controller, - v: view, - }), - err => controller.error(err), - ); - } else if (typeof result === "number") { - if (view && view.byteLength === result && view.buffer === controller.byobRequest?.view?.buffer) { - controller.byobRequest.respondWithNewView(view); - } else { - controller.byobRequest.respond(result); - } - } else if (result.constructor === $Uint8Array) { - controller.enqueue(result); - } - - if (closer[0] || result === false) { - $enqueueJob(callClose, controller); - closer[0] = false; - } - }; - - function createResult(tag, controller, view, closer) { - closer[0] = false; - - var result; - try { - result = pull(tag, view, closer); - } catch (err) { - return controller.error(err); - } - - return handleResult(result, controller, view); - } - - const registry = deinit ? new FinalizationRegistry(deinit) : null; - Prototype = class NativeReadableStreamSource { - constructor(tag, autoAllocateChunkSize, drainValue) { - this.#tag = tag; - this.#cancellationToken = {}; - this.pull = this.#pull.bind(this); - this.cancel = this.#cancel.bind(this); - this.autoAllocateChunkSize = autoAllocateChunkSize; - - if (drainValue !== undefined) { - this.start = controller => { - controller.enqueue(drainValue); - }; - } - - if (registry) { - registry.register(this, tag, this.#cancellationToken); - } - } - - #cancellationToken; - pull; - cancel; - start; - - #tag; - type = "bytes"; - autoAllocateChunkSize = 0; - - static startSync = start; - - #pull(controller) { - var tag = this.#tag; - - if (!tag) { - controller.close(); - return; - } - - createResult(tag, controller, controller.byobRequest.view, closer); - } - - #cancel(reason) { - var tag = this.#tag; - - registry && registry.unregister(this.#cancellationToken); - setRefOrUnref && setRefOrUnref(tag, false); - cancel(tag, reason); - } - static deinit = deinit; - static drain = drain; - }; - $lazyStreamPrototypeMap.$set(nativeType, Prototype); - } - - const chunkSize = Prototype.startSync(nativePtr, autoAllocateChunkSize); - var drainValue; - const { drain: drainFn, deinit: deinitFn } = Prototype; - if (drainFn) { - drainValue = drainFn(nativePtr); - } - - // empty file, no need for native back-and-forth on this - if (chunkSize === 0) { - deinit && nativePtr && $enqueueJob(deinit, nativePtr); - - if ((drainValue?.byteLength ?? 0) > 0) { - return { - start(controller) { - controller.enqueue(drainValue); - controller.close(); - }, - type: "bytes", - }; - } - - return { - start(controller) { - controller.close(); - }, - type: "bytes", - }; - } - - return new Prototype(nativePtr, chunkSize, drainValue); -} - -export function readableStreamIntoArray(stream) { - var reader = stream.getReader(); - var manyResult = reader.readMany(); - - async function processManyResult(result) { - if (result.done) { - return []; - } - - var chunks = result.value || []; - - while (true) { - var thisResult = await reader.read(); - if (thisResult.done) { - break; - } - chunks = chunks.concat(thisResult.value); - } - - return chunks; - } - - if (manyResult && $isPromise(manyResult)) { - return manyResult.$then(processManyResult); - } - - return processManyResult(manyResult); -} - -export function readableStreamIntoText(stream) { - const [textStream, closer] = $createTextStream($getByIdDirectPrivate(stream, "highWaterMark")); - const prom = $readStreamIntoSink(stream, textStream, false); - if (prom && $isPromise(prom)) { - return Promise.$resolve(prom).$then(closer.$promise); - } - return closer.$promise; -} - -export function readableStreamToArrayBufferDirect(stream, underlyingSource) { - var sink = new $Bun.ArrayBufferSink(); - $putByIdDirectPrivate(stream, "underlyingSource", undefined); - var highWaterMark = $getByIdDirectPrivate(stream, "highWaterMark"); - sink.start(highWaterMark ? { highWaterMark } : {}); - var capability = $newPromiseCapability(Promise); - var ended = false; - var pull = underlyingSource.pull; - var close = underlyingSource.close; - - var controller = { - start() {}, - close(reason) { - if (!ended) { - ended = true; - if (close) { - close(); - } - - $fulfillPromise(capability.$promise, sink.end()); - } - }, - end() { - if (!ended) { - ended = true; - if (close) { - close(); - } - $fulfillPromise(capability.$promise, sink.end()); - } - }, - flush() { - return 0; - }, - write: sink.write.bind(sink), - }; - - var didError = false; - try { - const firstPull = pull(controller); - if (firstPull && $isObject(firstPull) && $isPromise(firstPull)) { - return (async function (controller, promise, pull) { - while (!ended) { - await pull(controller); - } - return await promise; - })(controller, promise, pull); - } - - return capability.$promise; - } catch (e) { - didError = true; - $readableStreamError(stream, e); - return Promise.$reject(e); - } finally { - if (!didError && stream) $readableStreamClose(stream); - controller = close = sink = pull = stream = undefined; - } -} - -export async function readableStreamToTextDirect(stream, underlyingSource) { - const capability = $initializeTextStream.$call(stream, underlyingSource, undefined); - var reader = stream.getReader(); - - while ($getByIdDirectPrivate(stream, "state") === $streamReadable) { - var thisResult = await reader.read(); - if (thisResult.done) { - break; - } - } - - try { - reader.releaseLock(); - } catch (e) {} - reader = undefined; - stream = undefined; - - return capability.$promise; -} - -export async function readableStreamToArrayDirect(stream, underlyingSource) { - const capability = $initializeArrayStream.$call(stream, underlyingSource, undefined); - underlyingSource = undefined; - var reader = stream.getReader(); - try { - while ($getByIdDirectPrivate(stream, "state") === $streamReadable) { - var thisResult = await reader.read(); - if (thisResult.done) { - break; - } - } - - try { - reader.releaseLock(); - } catch (e) {} - reader = undefined; - - return Promise.$resolve(capability.$promise); - } catch (e) { - throw e; - } finally { - stream = undefined; - reader = undefined; - } -} - -export function readableStreamDefineLazyIterators(prototype) { - var asyncIterator = globalThis.Symbol.asyncIterator; - - var ReadableStreamAsyncIterator = async function* ReadableStreamAsyncIterator(stream, preventCancel) { - var reader = stream.getReader(); - var deferredError; - try { - while (true) { - var done, value; - const firstResult = reader.readMany(); - if ($isPromise(firstResult)) { - ({ done, value } = await firstResult); - } else { - ({ done, value } = firstResult); - } - - if (done) { - return; - } - yield* value; - } - } catch (e) { - deferredError = e; - } finally { - reader.releaseLock(); - - if (!preventCancel) { - stream.cancel(deferredError); - } - - if (deferredError) { - throw deferredError; - } - } - }; - var createAsyncIterator = function asyncIterator() { - return ReadableStreamAsyncIterator(this, false); - }; - var createValues = function values({ preventCancel = false } = { preventCancel: false }) { - return ReadableStreamAsyncIterator(this, preventCancel); - }; - $Object.$defineProperty(prototype, asyncIterator, { value: createAsyncIterator }); - $Object.$defineProperty(prototype, "values", { value: createValues }); - return prototype; -} diff --git a/src/bun.js/builtins/ts/StreamInternals.ts b/src/bun.js/builtins/ts/StreamInternals.ts deleted file mode 100644 index b42dc2f57..000000000 --- a/src/bun.js/builtins/ts/StreamInternals.ts +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. - * Copyright (C) 2015 Igalia. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// @internal - -export function markPromiseAsHandled(promise: Promise<unknown>) { - $assert($isPromise(promise)); - $putPromiseInternalField( - promise, - $promiseFieldFlags, - $getPromiseInternalField(promise, $promiseFieldFlags) | $promiseFlagsIsHandled, - ); -} - -export function shieldingPromiseResolve(result) { - const promise = Promise.$resolve(result); - if (promise.$then === undefined) promise.$then = Promise.prototype.$then; - return promise; -} - -export function promiseInvokeOrNoopMethodNoCatch(object, method, args) { - if (method === undefined) return Promise.$resolve(); - return $shieldingPromiseResolve(method.$apply(object, args)); -} - -export function promiseInvokeOrNoopNoCatch(object, key, args) { - return $promiseInvokeOrNoopMethodNoCatch(object, object[key], args); -} - -export function promiseInvokeOrNoopMethod(object, method, args) { - try { - return $promiseInvokeOrNoopMethodNoCatch(object, method, args); - } catch (error) { - return Promise.$reject(error); - } -} - -export function promiseInvokeOrNoop(object, key, args) { - try { - return $promiseInvokeOrNoopNoCatch(object, key, args); - } catch (error) { - return Promise.$reject(error); - } -} - -export function promiseInvokeOrFallbackOrNoop(object, key1, args1, key2, args2) { - try { - const method = object[key1]; - if (method === undefined) return $promiseInvokeOrNoopNoCatch(object, key2, args2); - return $shieldingPromiseResolve(method.$apply(object, args1)); - } catch (error) { - return Promise.$reject(error); - } -} - -export function validateAndNormalizeQueuingStrategy(size, highWaterMark) { - if (size !== undefined && typeof size !== "function") throw new TypeError("size parameter must be a function"); - - const newHighWaterMark = $toNumber(highWaterMark); - - if (isNaN(newHighWaterMark) || newHighWaterMark < 0) - throw new RangeError("highWaterMark value is negative or not a number"); - - return { size: size, highWaterMark: newHighWaterMark }; -} - -$linkTimeConstant; -export function createFIFO() { - var slice = Array.prototype.slice; - - class Denqueue { - constructor() { - this._head = 0; - this._tail = 0; - // this._capacity = 0; - this._capacityMask = 0x3; - this._list = $newArrayWithSize(4); - } - - _head; - _tail; - _capacityMask; - _list; - - size() { - if (this._head === this._tail) return 0; - if (this._head < this._tail) return this._tail - this._head; - else return this._capacityMask + 1 - (this._head - this._tail); - } - - isEmpty() { - return this.size() == 0; - } - - isNotEmpty() { - return this.size() > 0; - } - - shift() { - var { _head: head, _tail, _list, _capacityMask } = this; - if (head === _tail) return undefined; - var item = _list[head]; - $putByValDirect(_list, head, undefined); - head = this._head = (head + 1) & _capacityMask; - if (head < 2 && _tail > 10000 && _tail <= _list.length >>> 2) this._shrinkArray(); - return item; - } - - peek() { - if (this._head === this._tail) return undefined; - return this._list[this._head]; - } - - push(item) { - var tail = this._tail; - $putByValDirect(this._list, tail, item); - this._tail = (tail + 1) & this._capacityMask; - if (this._tail === this._head) { - this._growArray(); - } - // if (this._capacity && this.size() > this._capacity) { - // this.shift(); - // } - } - - toArray(fullCopy) { - var list = this._list; - var len = $toLength(list.length); - - if (fullCopy || this._head > this._tail) { - var _head = $toLength(this._head); - var _tail = $toLength(this._tail); - var total = $toLength(len - _head + _tail); - var array = $newArrayWithSize(total); - var j = 0; - for (var i = _head; i < len; i++) $putByValDirect(array, j++, list[i]); - for (var i = 0; i < _tail; i++) $putByValDirect(array, j++, list[i]); - return array; - } else { - return slice.$call(list, this._head, this._tail); - } - } - - clear() { - this._head = 0; - this._tail = 0; - this._list.fill(undefined); - } - - _growArray() { - if (this._head) { - // copy existing data, head to end, then beginning to tail. - this._list = this.toArray(true); - this._head = 0; - } - - // head is at 0 and array is now full, safe to extend - this._tail = $toLength(this._list.length); - - this._list.length <<= 1; - this._capacityMask = (this._capacityMask << 1) | 1; - } - - shrinkArray() { - this._list.length >>>= 1; - this._capacityMask >>>= 1; - } - } - - return new Denqueue(); -} - -export function newQueue() { - return { content: $createFIFO(), size: 0 }; -} - -export function dequeueValue(queue) { - const record = queue.content.shift(); - queue.size -= record.size; - // As described by spec, below case may occur due to rounding errors. - if (queue.size < 0) queue.size = 0; - return record.value; -} - -export function enqueueValueWithSize(queue, value, size) { - size = $toNumber(size); - if (!isFinite(size) || size < 0) throw new RangeError("size has an incorrect value"); - - queue.content.push({ value, size }); - queue.size += size; -} - -export function peekQueueValue(queue) { - return queue.content.peek()?.value; -} - -export function resetQueue(queue) { - $assert("content" in queue); - $assert("size" in queue); - queue.content.clear(); - queue.size = 0; -} - -export function extractSizeAlgorithm(strategy) { - const sizeAlgorithm = strategy.size; - - if (sizeAlgorithm === undefined) return () => 1; - - if (typeof sizeAlgorithm !== "function") throw new TypeError("strategy.size must be a function"); - - return chunk => { - return sizeAlgorithm(chunk); - }; -} - -export function extractHighWaterMark(strategy, defaultHWM) { - const highWaterMark = strategy.highWaterMark; - - if (highWaterMark === undefined) return defaultHWM; - - if (isNaN(highWaterMark) || highWaterMark < 0) - throw new RangeError("highWaterMark value is negative or not a number"); - - return $toNumber(highWaterMark); -} - -export function extractHighWaterMarkFromQueuingStrategyInit(init: { highWaterMark?: number }) { - if (!$isObject(init)) throw new TypeError("QueuingStrategyInit argument must be an object."); - const { highWaterMark } = init; - if (highWaterMark === undefined) throw new TypeError("QueuingStrategyInit.highWaterMark member is required."); - - return $toNumber(highWaterMark); -} - -export function createFulfilledPromise(value) { - const promise = $newPromise(); - $fulfillPromise(promise, value); - return promise; -} - -export function toDictionary(value, defaultValue, errorMessage) { - if (value === undefined || value === null) return defaultValue; - if (!$isObject(value)) throw new TypeError(errorMessage); - return value; -} diff --git a/src/bun.js/builtins/ts/TransformStream.ts b/src/bun.js/builtins/ts/TransformStream.ts deleted file mode 100644 index 54467db39..000000000 --- a/src/bun.js/builtins/ts/TransformStream.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2020 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeTransformStream(this) { - let transformer = arguments[0]; - - // This is the path for CreateTransformStream. - if ($isObject(transformer) && $getByIdDirectPrivate(transformer, "TransformStream")) return this; - - let writableStrategy = arguments[1]; - let readableStrategy = arguments[2]; - - if (transformer === undefined) transformer = null; - - if (readableStrategy === undefined) readableStrategy = {}; - - if (writableStrategy === undefined) writableStrategy = {}; - - let transformerDict = {}; - if (transformer !== null) { - if ("start" in transformer) { - transformerDict["start"] = transformer["start"]; - if (typeof transformerDict["start"] !== "function") $throwTypeError("transformer.start should be a function"); - } - if ("transform" in transformer) { - transformerDict["transform"] = transformer["transform"]; - if (typeof transformerDict["transform"] !== "function") - $throwTypeError("transformer.transform should be a function"); - } - if ("flush" in transformer) { - transformerDict["flush"] = transformer["flush"]; - if (typeof transformerDict["flush"] !== "function") $throwTypeError("transformer.flush should be a function"); - } - - if ("readableType" in transformer) throw new RangeError("TransformStream transformer has a readableType"); - if ("writableType" in transformer) throw new RangeError("TransformStream transformer has a writableType"); - } - - const readableHighWaterMark = $extractHighWaterMark(readableStrategy, 0); - const readableSizeAlgorithm = $extractSizeAlgorithm(readableStrategy); - - const writableHighWaterMark = $extractHighWaterMark(writableStrategy, 1); - const writableSizeAlgorithm = $extractSizeAlgorithm(writableStrategy); - - const startPromiseCapability = $newPromiseCapability(Promise); - $initializeTransformStream( - this, - startPromiseCapability.$promise, - writableHighWaterMark, - writableSizeAlgorithm, - readableHighWaterMark, - readableSizeAlgorithm, - ); - $setUpTransformStreamDefaultControllerFromTransformer(this, transformer, transformerDict); - - if ("start" in transformerDict) { - const controller = $getByIdDirectPrivate(this, "controller"); - const startAlgorithm = () => $promiseInvokeOrNoopMethodNoCatch(transformer, transformerDict["start"], [controller]); - startAlgorithm().$then( - () => { - // FIXME: We probably need to resolve start promise with the result of the start algorithm. - startPromiseCapability.$resolve.$call(); - }, - error => { - startPromiseCapability.$reject.$call(undefined, error); - }, - ); - } else startPromiseCapability.$resolve.$call(); - - return this; -} - -$getter; -export function readable() { - if (!$isTransformStream(this)) throw $makeThisTypeError("TransformStream", "readable"); - - return $getByIdDirectPrivate(this, "readable"); -} - -export function writable() { - if (!$isTransformStream(this)) throw $makeThisTypeError("TransformStream", "writable"); - - return $getByIdDirectPrivate(this, "writable"); -} diff --git a/src/bun.js/builtins/ts/TransformStreamDefaultController.ts b/src/bun.js/builtins/ts/TransformStreamDefaultController.ts deleted file mode 100644 index 1045498b8..000000000 --- a/src/bun.js/builtins/ts/TransformStreamDefaultController.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2020 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeTransformStreamDefaultController(this) { - return this; -} - -$getter; -export function desiredSize(this) { - if (!$isTransformStreamDefaultController(this)) - throw $makeThisTypeError("TransformStreamDefaultController", "enqueue"); - - const stream = $getByIdDirectPrivate(this, "stream"); - const readable = $getByIdDirectPrivate(stream, "readable"); - const readableController = $getByIdDirectPrivate(readable, "readableStreamController"); - - return $readableStreamDefaultControllerGetDesiredSize(readableController); -} - -export function enqueue(this, chunk) { - if (!$isTransformStreamDefaultController(this)) - throw $makeThisTypeError("TransformStreamDefaultController", "enqueue"); - - $transformStreamDefaultControllerEnqueue(this, chunk); -} - -export function error(this, e) { - if (!$isTransformStreamDefaultController(this)) throw $makeThisTypeError("TransformStreamDefaultController", "error"); - - $transformStreamDefaultControllerError(this, e); -} - -export function terminate(this) { - if (!$isTransformStreamDefaultController(this)) - throw $makeThisTypeError("TransformStreamDefaultController", "terminate"); - - $transformStreamDefaultControllerTerminate(this); -} diff --git a/src/bun.js/builtins/ts/TransformStreamInternals.ts b/src/bun.js/builtins/ts/TransformStreamInternals.ts deleted file mode 100644 index 9994d1282..000000000 --- a/src/bun.js/builtins/ts/TransformStreamInternals.ts +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2020 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -// @internal - -export function isTransformStream(stream) { - return $isObject(stream) && !!$getByIdDirectPrivate(stream, "readable"); -} - -export function isTransformStreamDefaultController(controller) { - return $isObject(controller) && !!$getByIdDirectPrivate(controller, "transformAlgorithm"); -} - -export function createTransformStream( - startAlgorithm, - transformAlgorithm, - flushAlgorithm, - writableHighWaterMark, - writableSizeAlgorithm, - readableHighWaterMark, - readableSizeAlgorithm, -) { - if (writableHighWaterMark === undefined) writableHighWaterMark = 1; - if (writableSizeAlgorithm === undefined) writableSizeAlgorithm = () => 1; - if (readableHighWaterMark === undefined) readableHighWaterMark = 0; - if (readableSizeAlgorithm === undefined) readableSizeAlgorithm = () => 1; - $assert(writableHighWaterMark >= 0); - $assert(readableHighWaterMark >= 0); - - const transform = {}; - $putByIdDirectPrivate(transform, "TransformStream", true); - - const stream = new TransformStream(transform); - const startPromiseCapability = $newPromiseCapability(Promise); - $initializeTransformStream( - stream, - startPromiseCapability.$promise, - writableHighWaterMark, - writableSizeAlgorithm, - readableHighWaterMark, - readableSizeAlgorithm, - ); - - const controller = new TransformStreamDefaultController(); - $setUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm); - - startAlgorithm().$then( - () => { - startPromiseCapability.$resolve.$call(); - }, - error => { - startPromiseCapability.$reject.$call(undefined, error); - }, - ); - - return stream; -} - -export function initializeTransformStream( - stream, - startPromise, - writableHighWaterMark, - writableSizeAlgorithm, - readableHighWaterMark, - readableSizeAlgorithm, -) { - const startAlgorithm = () => { - return startPromise; - }; - const writeAlgorithm = chunk => { - return $transformStreamDefaultSinkWriteAlgorithm(stream, chunk); - }; - const abortAlgorithm = reason => { - return $transformStreamDefaultSinkAbortAlgorithm(stream, reason); - }; - const closeAlgorithm = () => { - return $transformStreamDefaultSinkCloseAlgorithm(stream); - }; - const writable = $createWritableStream( - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - writableHighWaterMark, - writableSizeAlgorithm, - ); - - const pullAlgorithm = () => { - return $transformStreamDefaultSourcePullAlgorithm(stream); - }; - const cancelAlgorithm = reason => { - $transformStreamErrorWritableAndUnblockWrite(stream, reason); - return Promise.$resolve(); - }; - const underlyingSource = {}; - $putByIdDirectPrivate(underlyingSource, "start", startAlgorithm); - $putByIdDirectPrivate(underlyingSource, "pull", pullAlgorithm); - $putByIdDirectPrivate(underlyingSource, "cancel", cancelAlgorithm); - const options = {}; - $putByIdDirectPrivate(options, "size", readableSizeAlgorithm); - $putByIdDirectPrivate(options, "highWaterMark", readableHighWaterMark); - const readable = new ReadableStream(underlyingSource, options); - - // The writable to expose to JS through writable getter. - $putByIdDirectPrivate(stream, "writable", writable); - // The writable to use for the actual transform algorithms. - $putByIdDirectPrivate(stream, "internalWritable", $getInternalWritableStream(writable)); - - $putByIdDirectPrivate(stream, "readable", readable); - $putByIdDirectPrivate(stream, "backpressure", undefined); - $putByIdDirectPrivate(stream, "backpressureChangePromise", undefined); - - $transformStreamSetBackpressure(stream, true); - $putByIdDirectPrivate(stream, "controller", undefined); -} - -export function transformStreamError(stream, e) { - const readable = $getByIdDirectPrivate(stream, "readable"); - const readableController = $getByIdDirectPrivate(readable, "readableStreamController"); - $readableStreamDefaultControllerError(readableController, e); - - $transformStreamErrorWritableAndUnblockWrite(stream, e); -} - -export function transformStreamErrorWritableAndUnblockWrite(stream, e) { - $transformStreamDefaultControllerClearAlgorithms($getByIdDirectPrivate(stream, "controller")); - - const writable = $getByIdDirectPrivate(stream, "internalWritable"); - $writableStreamDefaultControllerErrorIfNeeded($getByIdDirectPrivate(writable, "controller"), e); - - if ($getByIdDirectPrivate(stream, "backpressure")) $transformStreamSetBackpressure(stream, false); -} - -export function transformStreamSetBackpressure(stream, backpressure) { - $assert($getByIdDirectPrivate(stream, "backpressure") !== backpressure); - - const backpressureChangePromise = $getByIdDirectPrivate(stream, "backpressureChangePromise"); - if (backpressureChangePromise !== undefined) backpressureChangePromise.$resolve.$call(); - - $putByIdDirectPrivate(stream, "backpressureChangePromise", $newPromiseCapability(Promise)); - $putByIdDirectPrivate(stream, "backpressure", backpressure); -} - -export function setUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm) { - $assert($isTransformStream(stream)); - $assert($getByIdDirectPrivate(stream, "controller") === undefined); - - $putByIdDirectPrivate(controller, "stream", stream); - $putByIdDirectPrivate(stream, "controller", controller); - $putByIdDirectPrivate(controller, "transformAlgorithm", transformAlgorithm); - $putByIdDirectPrivate(controller, "flushAlgorithm", flushAlgorithm); -} - -export function setUpTransformStreamDefaultControllerFromTransformer(stream, transformer, transformerDict) { - const controller = new TransformStreamDefaultController(); - let transformAlgorithm = chunk => { - try { - $transformStreamDefaultControllerEnqueue(controller, chunk); - } catch (e) { - return Promise.$reject(e); - } - return Promise.$resolve(); - }; - let flushAlgorithm = () => { - return Promise.$resolve(); - }; - - if ("transform" in transformerDict) - transformAlgorithm = chunk => { - return $promiseInvokeOrNoopMethod(transformer, transformerDict["transform"], [chunk, controller]); - }; - - if ("flush" in transformerDict) { - flushAlgorithm = () => { - return $promiseInvokeOrNoopMethod(transformer, transformerDict["flush"], [controller]); - }; - } - - $setUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm); -} - -export function transformStreamDefaultControllerClearAlgorithms(controller) { - // We set transformAlgorithm to true to allow GC but keep the isTransformStreamDefaultController check. - $putByIdDirectPrivate(controller, "transformAlgorithm", true); - $putByIdDirectPrivate(controller, "flushAlgorithm", undefined); -} - -export function transformStreamDefaultControllerEnqueue(controller, chunk) { - const stream = $getByIdDirectPrivate(controller, "stream"); - const readable = $getByIdDirectPrivate(stream, "readable"); - const readableController = $getByIdDirectPrivate(readable, "readableStreamController"); - - $assert(readableController !== undefined); - if (!$readableStreamDefaultControllerCanCloseOrEnqueue(readableController)) - $throwTypeError("TransformStream.readable cannot close or enqueue"); - - try { - $readableStreamDefaultControllerEnqueue(readableController, chunk); - } catch (e) { - $transformStreamErrorWritableAndUnblockWrite(stream, e); - throw $getByIdDirectPrivate(readable, "storedError"); - } - - const backpressure = !$readableStreamDefaultControllerShouldCallPull(readableController); - if (backpressure !== $getByIdDirectPrivate(stream, "backpressure")) { - $assert(backpressure); - $transformStreamSetBackpressure(stream, true); - } -} - -export function transformStreamDefaultControllerError(controller, e) { - $transformStreamError($getByIdDirectPrivate(controller, "stream"), e); -} - -export function transformStreamDefaultControllerPerformTransform(controller, chunk) { - const promiseCapability = $newPromiseCapability(Promise); - - const transformPromise = $getByIdDirectPrivate(controller, "transformAlgorithm").$call(undefined, chunk); - transformPromise.$then( - () => { - promiseCapability.$resolve(); - }, - r => { - $transformStreamError($getByIdDirectPrivate(controller, "stream"), r); - promiseCapability.$reject.$call(undefined, r); - }, - ); - return promiseCapability.$promise; -} - -export function transformStreamDefaultControllerTerminate(controller) { - const stream = $getByIdDirectPrivate(controller, "stream"); - const readable = $getByIdDirectPrivate(stream, "readable"); - const readableController = $getByIdDirectPrivate(readable, "readableStreamController"); - - // FIXME: Update readableStreamDefaultControllerClose to make this check. - if ($readableStreamDefaultControllerCanCloseOrEnqueue(readableController)) - $readableStreamDefaultControllerClose(readableController); - const error = $makeTypeError("the stream has been terminated"); - $transformStreamErrorWritableAndUnblockWrite(stream, error); -} - -export function transformStreamDefaultSinkWriteAlgorithm(stream, chunk) { - const writable = $getByIdDirectPrivate(stream, "internalWritable"); - - $assert($getByIdDirectPrivate(writable, "state") === "writable"); - - const controller = $getByIdDirectPrivate(stream, "controller"); - - if ($getByIdDirectPrivate(stream, "backpressure")) { - const promiseCapability = $newPromiseCapability(Promise); - - const backpressureChangePromise = $getByIdDirectPrivate(stream, "backpressureChangePromise"); - $assert(backpressureChangePromise !== undefined); - backpressureChangePromise.$promise.$then( - () => { - const state = $getByIdDirectPrivate(writable, "state"); - if (state === "erroring") { - promiseCapability.$reject.$call(undefined, $getByIdDirectPrivate(writable, "storedError")); - return; - } - - $assert(state === "writable"); - $transformStreamDefaultControllerPerformTransform(controller, chunk).$then( - () => { - promiseCapability.$resolve(); - }, - e => { - promiseCapability.$reject.$call(undefined, e); - }, - ); - }, - e => { - promiseCapability.$reject.$call(undefined, e); - }, - ); - - return promiseCapability.$promise; - } - return $transformStreamDefaultControllerPerformTransform(controller, chunk); -} - -export function transformStreamDefaultSinkAbortAlgorithm(stream, reason) { - $transformStreamError(stream, reason); - return Promise.$resolve(); -} - -export function transformStreamDefaultSinkCloseAlgorithm(stream) { - const readable = $getByIdDirectPrivate(stream, "readable"); - const controller = $getByIdDirectPrivate(stream, "controller"); - const readableController = $getByIdDirectPrivate(readable, "readableStreamController"); - - const flushAlgorithm = $getByIdDirectPrivate(controller, "flushAlgorithm"); - $assert(flushAlgorithm !== undefined); - const flushPromise = $getByIdDirectPrivate(controller, "flushAlgorithm").$call(); - $transformStreamDefaultControllerClearAlgorithms(controller); - - const promiseCapability = $newPromiseCapability(Promise); - flushPromise.$then( - () => { - if ($getByIdDirectPrivate(readable, "state") === $streamErrored) { - promiseCapability.$reject.$call(undefined, $getByIdDirectPrivate(readable, "storedError")); - return; - } - - // FIXME: Update readableStreamDefaultControllerClose to make this check. - if ($readableStreamDefaultControllerCanCloseOrEnqueue(readableController)) - $readableStreamDefaultControllerClose(readableController); - promiseCapability.$resolve(); - }, - r => { - $transformStreamError($getByIdDirectPrivate(controller, "stream"), r); - promiseCapability.$reject.$call(undefined, $getByIdDirectPrivate(readable, "storedError")); - }, - ); - return promiseCapability.$promise; -} - -export function transformStreamDefaultSourcePullAlgorithm(stream) { - $assert($getByIdDirectPrivate(stream, "backpressure")); - $assert($getByIdDirectPrivate(stream, "backpressureChangePromise") !== undefined); - - $transformStreamSetBackpressure(stream, false); - - return $getByIdDirectPrivate(stream, "backpressureChangePromise").$promise; -} diff --git a/src/bun.js/builtins/ts/WritableStreamDefaultController.ts b/src/bun.js/builtins/ts/WritableStreamDefaultController.ts deleted file mode 100644 index 1a3ddc290..000000000 --- a/src/bun.js/builtins/ts/WritableStreamDefaultController.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2020 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeWritableStreamDefaultController(this) { - $putByIdDirectPrivate(this, "queue", $newQueue()); - $putByIdDirectPrivate(this, "abortSteps", reason => { - const result = $getByIdDirectPrivate(this, "abortAlgorithm").$call(undefined, reason); - $writableStreamDefaultControllerClearAlgorithms(this); - return result; - }); - - $putByIdDirectPrivate(this, "errorSteps", () => { - $resetQueue($getByIdDirectPrivate(this, "queue")); - }); - - return this; -} - -export function error(this, e) { - if ($getByIdDirectPrivate(this, "abortSteps") === undefined) - throw $makeThisTypeError("WritableStreamDefaultController", "error"); - - const stream = $getByIdDirectPrivate(this, "stream"); - if ($getByIdDirectPrivate(stream, "state") !== "writable") return; - $writableStreamDefaultControllerError(this, e); -} diff --git a/src/bun.js/builtins/ts/WritableStreamDefaultWriter.ts b/src/bun.js/builtins/ts/WritableStreamDefaultWriter.ts deleted file mode 100644 index 795b43892..000000000 --- a/src/bun.js/builtins/ts/WritableStreamDefaultWriter.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2020 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -export function initializeWritableStreamDefaultWriter(stream) { - // stream can be a WritableStream if WritableStreamDefaultWriter constructor is called directly from JS - // or an InternalWritableStream in other code paths. - const internalStream = $getInternalWritableStream(stream); - if (internalStream) stream = internalStream; - - if (!$isWritableStream(stream)) $throwTypeError("WritableStreamDefaultWriter constructor takes a WritableStream"); - - $setUpWritableStreamDefaultWriter(this, stream); - return this; -} - -$getter; -export function closed() { - if (!$isWritableStreamDefaultWriter(this)) - return Promise.$reject($makeGetterTypeError("WritableStreamDefaultWriter", "closed")); - - return $getByIdDirectPrivate(this, "closedPromise").$promise; -} - -$getter; -export function desiredSize() { - if (!$isWritableStreamDefaultWriter(this)) throw $makeThisTypeError("WritableStreamDefaultWriter", "desiredSize"); - - if ($getByIdDirectPrivate(this, "stream") === undefined) $throwTypeError("WritableStreamDefaultWriter has no stream"); - - return $writableStreamDefaultWriterGetDesiredSize(this); -} - -$getter; -export function ready() { - if (!$isWritableStreamDefaultWriter(this)) - return Promise.$reject($makeThisTypeError("WritableStreamDefaultWriter", "ready")); - - return $getByIdDirectPrivate(this, "readyPromise").$promise; -} - -export function abort(reason) { - if (!$isWritableStreamDefaultWriter(this)) - return Promise.$reject($makeThisTypeError("WritableStreamDefaultWriter", "abort")); - - if ($getByIdDirectPrivate(this, "stream") === undefined) - return Promise.$reject($makeTypeError("WritableStreamDefaultWriter has no stream")); - - return $writableStreamDefaultWriterAbort(this, reason); -} - -export function close() { - if (!$isWritableStreamDefaultWriter(this)) - return Promise.$reject($makeThisTypeError("WritableStreamDefaultWriter", "close")); - - const stream = $getByIdDirectPrivate(this, "stream"); - if (stream === undefined) return Promise.$reject($makeTypeError("WritableStreamDefaultWriter has no stream")); - - if ($writableStreamCloseQueuedOrInFlight(stream)) - return Promise.$reject($makeTypeError("WritableStreamDefaultWriter is being closed")); - - return $writableStreamDefaultWriterClose(this); -} - -export function releaseLock() { - if (!$isWritableStreamDefaultWriter(this)) throw $makeThisTypeError("WritableStreamDefaultWriter", "releaseLock"); - - const stream = $getByIdDirectPrivate(this, "stream"); - if (stream === undefined) return; - - $assert($getByIdDirectPrivate(stream, "writer") !== undefined); - $writableStreamDefaultWriterRelease(this); -} - -export function write(chunk) { - if (!$isWritableStreamDefaultWriter(this)) - return Promise.$reject($makeThisTypeError("WritableStreamDefaultWriter", "write")); - - if ($getByIdDirectPrivate(this, "stream") === undefined) - return Promise.$reject($makeTypeError("WritableStreamDefaultWriter has no stream")); - - return $writableStreamDefaultWriterWrite(this, chunk); -} diff --git a/src/bun.js/builtins/ts/WritableStreamInternals.ts b/src/bun.js/builtins/ts/WritableStreamInternals.ts deleted file mode 100644 index f436a285e..000000000 --- a/src/bun.js/builtins/ts/WritableStreamInternals.ts +++ /dev/null @@ -1,790 +0,0 @@ -/* - * Copyright (C) 2015 Canon Inc. - * Copyright (C) 2015 Igalia - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// @internal - -export function isWritableStream(stream) { - return $isObject(stream) && !!$getByIdDirectPrivate(stream, "underlyingSink"); -} - -export function isWritableStreamDefaultWriter(writer) { - return $isObject(writer) && !!$getByIdDirectPrivate(writer, "closedPromise"); -} - -export function acquireWritableStreamDefaultWriter(stream) { - return new WritableStreamDefaultWriter(stream); -} - -// https://streams.spec.whatwg.org/#create-writable-stream -export function createWritableStream( - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - highWaterMark, - sizeAlgorithm, -) { - $assert(typeof highWaterMark === "number" && !isNaN(highWaterMark) && highWaterMark >= 0); - - const internalStream = {}; - $initializeWritableStreamSlots(internalStream, {}); - const controller = new WritableStreamDefaultController(); - - $setUpWritableStreamDefaultController( - internalStream, - controller, - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - highWaterMark, - sizeAlgorithm, - ); - - return $createWritableStreamFromInternal(internalStream); -} - -export function createInternalWritableStreamFromUnderlyingSink(underlyingSink, strategy) { - const stream = {}; - - if (underlyingSink === undefined) underlyingSink = {}; - - if (strategy === undefined) strategy = {}; - - if (!$isObject(underlyingSink)) $throwTypeError("WritableStream constructor takes an object as first argument"); - - if ("type" in underlyingSink) $throwRangeError("Invalid type is specified"); - - const sizeAlgorithm = $extractSizeAlgorithm(strategy); - const highWaterMark = $extractHighWaterMark(strategy, 1); - - const underlyingSinkDict = {}; - if ("start" in underlyingSink) { - underlyingSinkDict["start"] = underlyingSink["start"]; - if (typeof underlyingSinkDict["start"] !== "function") $throwTypeError("underlyingSink.start should be a function"); - } - if ("write" in underlyingSink) { - underlyingSinkDict["write"] = underlyingSink["write"]; - if (typeof underlyingSinkDict["write"] !== "function") $throwTypeError("underlyingSink.write should be a function"); - } - if ("close" in underlyingSink) { - underlyingSinkDict["close"] = underlyingSink["close"]; - if (typeof underlyingSinkDict["close"] !== "function") $throwTypeError("underlyingSink.close should be a function"); - } - if ("abort" in underlyingSink) { - underlyingSinkDict["abort"] = underlyingSink["abort"]; - if (typeof underlyingSinkDict["abort"] !== "function") $throwTypeError("underlyingSink.abort should be a function"); - } - - $initializeWritableStreamSlots(stream, underlyingSink); - $setUpWritableStreamDefaultControllerFromUnderlyingSink( - stream, - underlyingSink, - underlyingSinkDict, - highWaterMark, - sizeAlgorithm, - ); - - return stream; -} - -export function initializeWritableStreamSlots(stream, underlyingSink) { - $putByIdDirectPrivate(stream, "state", "writable"); - $putByIdDirectPrivate(stream, "storedError", undefined); - $putByIdDirectPrivate(stream, "writer", undefined); - $putByIdDirectPrivate(stream, "controller", undefined); - $putByIdDirectPrivate(stream, "inFlightWriteRequest", undefined); - $putByIdDirectPrivate(stream, "closeRequest", undefined); - $putByIdDirectPrivate(stream, "inFlightCloseRequest", undefined); - $putByIdDirectPrivate(stream, "pendingAbortRequest", undefined); - $putByIdDirectPrivate(stream, "writeRequests", $createFIFO()); - $putByIdDirectPrivate(stream, "backpressure", false); - $putByIdDirectPrivate(stream, "underlyingSink", underlyingSink); -} - -export function writableStreamCloseForBindings(stream) { - if ($isWritableStreamLocked(stream)) - return Promise.$reject($makeTypeError("WritableStream.close method can only be used on non locked WritableStream")); - - if ($writableStreamCloseQueuedOrInFlight(stream)) - return Promise.$reject( - $makeTypeError("WritableStream.close method can only be used on a being close WritableStream"), - ); - - return $writableStreamClose(stream); -} - -export function writableStreamAbortForBindings(stream, reason) { - if ($isWritableStreamLocked(stream)) - return Promise.$reject($makeTypeError("WritableStream.abort method can only be used on non locked WritableStream")); - - return $writableStreamAbort(stream, reason); -} - -export function isWritableStreamLocked(stream) { - return $getByIdDirectPrivate(stream, "writer") !== undefined; -} - -export function setUpWritableStreamDefaultWriter(writer, stream) { - if ($isWritableStreamLocked(stream)) $throwTypeError("WritableStream is locked"); - - $putByIdDirectPrivate(writer, "stream", stream); - $putByIdDirectPrivate(stream, "writer", writer); - - const readyPromiseCapability = $newPromiseCapability(Promise); - const closedPromiseCapability = $newPromiseCapability(Promise); - $putByIdDirectPrivate(writer, "readyPromise", readyPromiseCapability); - $putByIdDirectPrivate(writer, "closedPromise", closedPromiseCapability); - - const state = $getByIdDirectPrivate(stream, "state"); - if (state === "writable") { - if ($writableStreamCloseQueuedOrInFlight(stream) || !$getByIdDirectPrivate(stream, "backpressure")) - readyPromiseCapability.$resolve.$call(); - } else if (state === "erroring") { - readyPromiseCapability.$reject.$call(undefined, $getByIdDirectPrivate(stream, "storedError")); - $markPromiseAsHandled(readyPromiseCapability.$promise); - } else if (state === "closed") { - readyPromiseCapability.$resolve.$call(); - closedPromiseCapability.$resolve.$call(); - } else { - $assert(state === "errored"); - const storedError = $getByIdDirectPrivate(stream, "storedError"); - readyPromiseCapability.$reject.$call(undefined, storedError); - $markPromiseAsHandled(readyPromiseCapability.$promise); - closedPromiseCapability.$reject.$call(undefined, storedError); - $markPromiseAsHandled(closedPromiseCapability.$promise); - } -} - -export function writableStreamAbort(stream, reason) { - const state = $getByIdDirectPrivate(stream, "state"); - if (state === "closed" || state === "errored") return Promise.$resolve(); - - const pendingAbortRequest = $getByIdDirectPrivate(stream, "pendingAbortRequest"); - if (pendingAbortRequest !== undefined) return pendingAbortRequest.promise.$promise; - - $assert(state === "writable" || state === "erroring"); - let wasAlreadyErroring = false; - if (state === "erroring") { - wasAlreadyErroring = true; - reason = undefined; - } - - const abortPromiseCapability = $newPromiseCapability(Promise); - $putByIdDirectPrivate(stream, "pendingAbortRequest", { - promise: abortPromiseCapability, - reason: reason, - wasAlreadyErroring: wasAlreadyErroring, - }); - - if (!wasAlreadyErroring) $writableStreamStartErroring(stream, reason); - return abortPromiseCapability.$promise; -} - -export function writableStreamClose(stream) { - const state = $getByIdDirectPrivate(stream, "state"); - if (state === "closed" || state === "errored") - return Promise.$reject($makeTypeError("Cannot close a writable stream that is closed or errored")); - - $assert(state === "writable" || state === "erroring"); - $assert(!$writableStreamCloseQueuedOrInFlight(stream)); - - const closePromiseCapability = $newPromiseCapability(Promise); - $putByIdDirectPrivate(stream, "closeRequest", closePromiseCapability); - - const writer = $getByIdDirectPrivate(stream, "writer"); - if (writer !== undefined && $getByIdDirectPrivate(stream, "backpressure") && state === "writable") - $getByIdDirectPrivate(writer, "readyPromise").$resolve.$call(); - - $writableStreamDefaultControllerClose($getByIdDirectPrivate(stream, "controller")); - - return closePromiseCapability.$promise; -} - -export function writableStreamAddWriteRequest(stream) { - $assert($isWritableStreamLocked(stream)); - $assert($getByIdDirectPrivate(stream, "state") === "writable"); - - const writePromiseCapability = $newPromiseCapability(Promise); - const writeRequests = $getByIdDirectPrivate(stream, "writeRequests"); - writeRequests.push(writePromiseCapability); - return writePromiseCapability.$promise; -} - -export function writableStreamCloseQueuedOrInFlight(stream) { - return ( - $getByIdDirectPrivate(stream, "closeRequest") !== undefined || - $getByIdDirectPrivate(stream, "inFlightCloseRequest") !== undefined - ); -} - -export function writableStreamDealWithRejection(stream, error) { - const state = $getByIdDirectPrivate(stream, "state"); - if (state === "writable") { - $writableStreamStartErroring(stream, error); - return; - } - - $assert(state === "erroring"); - $writableStreamFinishErroring(stream); -} - -export function writableStreamFinishErroring(stream) { - $assert($getByIdDirectPrivate(stream, "state") === "erroring"); - $assert(!$writableStreamHasOperationMarkedInFlight(stream)); - - $putByIdDirectPrivate(stream, "state", "errored"); - - const controller = $getByIdDirectPrivate(stream, "controller"); - $getByIdDirectPrivate(controller, "errorSteps").$call(); - - const storedError = $getByIdDirectPrivate(stream, "storedError"); - const requests = $getByIdDirectPrivate(stream, "writeRequests"); - for (var request = requests.shift(); request; request = requests.shift()) - request.$reject.$call(undefined, storedError); - - // TODO: is this still necessary? - $putByIdDirectPrivate(stream, "writeRequests", $createFIFO()); - - const abortRequest = $getByIdDirectPrivate(stream, "pendingAbortRequest"); - if (abortRequest === undefined) { - $writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); - return; - } - - $putByIdDirectPrivate(stream, "pendingAbortRequest", undefined); - if (abortRequest.wasAlreadyErroring) { - abortRequest.promise.$reject.$call(undefined, storedError); - $writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); - return; - } - - $getByIdDirectPrivate(controller, "abortSteps") - .$call(undefined, abortRequest.reason) - .$then( - () => { - abortRequest.promise.$resolve.$call(); - $writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); - }, - reason => { - abortRequest.promise.$reject.$call(undefined, reason); - $writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); - }, - ); -} - -export function writableStreamFinishInFlightClose(stream) { - const inFlightCloseRequest = $getByIdDirectPrivate(stream, "inFlightCloseRequest"); - inFlightCloseRequest.$resolve.$call(); - - $putByIdDirectPrivate(stream, "inFlightCloseRequest", undefined); - - const state = $getByIdDirectPrivate(stream, "state"); - $assert(state === "writable" || state === "erroring"); - - if (state === "erroring") { - $putByIdDirectPrivate(stream, "storedError", undefined); - const abortRequest = $getByIdDirectPrivate(stream, "pendingAbortRequest"); - if (abortRequest !== undefined) { - abortRequest.promise.$resolve.$call(); - $putByIdDirectPrivate(stream, "pendingAbortRequest", undefined); - } - } - - $putByIdDirectPrivate(stream, "state", "closed"); - - const writer = $getByIdDirectPrivate(stream, "writer"); - if (writer !== undefined) $getByIdDirectPrivate(writer, "closedPromise").$resolve.$call(); - - $assert($getByIdDirectPrivate(stream, "pendingAbortRequest") === undefined); - $assert($getByIdDirectPrivate(stream, "storedError") === undefined); -} - -export function writableStreamFinishInFlightCloseWithError(stream, error) { - const inFlightCloseRequest = $getByIdDirectPrivate(stream, "inFlightCloseRequest"); - $assert(inFlightCloseRequest !== undefined); - inFlightCloseRequest.$reject.$call(undefined, error); - - $putByIdDirectPrivate(stream, "inFlightCloseRequest", undefined); - - const state = $getByIdDirectPrivate(stream, "state"); - $assert(state === "writable" || state === "erroring"); - - const abortRequest = $getByIdDirectPrivate(stream, "pendingAbortRequest"); - if (abortRequest !== undefined) { - abortRequest.promise.$reject.$call(undefined, error); - $putByIdDirectPrivate(stream, "pendingAbortRequest", undefined); - } - - $writableStreamDealWithRejection(stream, error); -} - -export function writableStreamFinishInFlightWrite(stream) { - const inFlightWriteRequest = $getByIdDirectPrivate(stream, "inFlightWriteRequest"); - $assert(inFlightWriteRequest !== undefined); - inFlightWriteRequest.$resolve.$call(); - - $putByIdDirectPrivate(stream, "inFlightWriteRequest", undefined); -} - -export function writableStreamFinishInFlightWriteWithError(stream, error) { - const inFlightWriteRequest = $getByIdDirectPrivate(stream, "inFlightWriteRequest"); - $assert(inFlightWriteRequest !== undefined); - inFlightWriteRequest.$reject.$call(undefined, error); - - $putByIdDirectPrivate(stream, "inFlightWriteRequest", undefined); - - const state = $getByIdDirectPrivate(stream, "state"); - $assert(state === "writable" || state === "erroring"); - - $writableStreamDealWithRejection(stream, error); -} - -export function writableStreamHasOperationMarkedInFlight(stream) { - return ( - $getByIdDirectPrivate(stream, "inFlightWriteRequest") !== undefined || - $getByIdDirectPrivate(stream, "inFlightCloseRequest") !== undefined - ); -} - -export function writableStreamMarkCloseRequestInFlight(stream) { - const closeRequest = $getByIdDirectPrivate(stream, "closeRequest"); - $assert($getByIdDirectPrivate(stream, "inFlightCloseRequest") === undefined); - $assert(closeRequest !== undefined); - - $putByIdDirectPrivate(stream, "inFlightCloseRequest", closeRequest); - $putByIdDirectPrivate(stream, "closeRequest", undefined); -} - -export function writableStreamMarkFirstWriteRequestInFlight(stream) { - const writeRequests = $getByIdDirectPrivate(stream, "writeRequests"); - $assert($getByIdDirectPrivate(stream, "inFlightWriteRequest") === undefined); - $assert(writeRequests.isNotEmpty()); - - const writeRequest = writeRequests.shift(); - $putByIdDirectPrivate(stream, "inFlightWriteRequest", writeRequest); -} - -export function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { - $assert($getByIdDirectPrivate(stream, "state") === "errored"); - - const storedError = $getByIdDirectPrivate(stream, "storedError"); - - const closeRequest = $getByIdDirectPrivate(stream, "closeRequest"); - if (closeRequest !== undefined) { - $assert($getByIdDirectPrivate(stream, "inFlightCloseRequest") === undefined); - closeRequest.$reject.$call(undefined, storedError); - $putByIdDirectPrivate(stream, "closeRequest", undefined); - } - - const writer = $getByIdDirectPrivate(stream, "writer"); - if (writer !== undefined) { - const closedPromise = $getByIdDirectPrivate(writer, "closedPromise"); - closedPromise.$reject.$call(undefined, storedError); - $markPromiseAsHandled(closedPromise.$promise); - } -} - -export function writableStreamStartErroring(stream, reason) { - $assert($getByIdDirectPrivate(stream, "storedError") === undefined); - $assert($getByIdDirectPrivate(stream, "state") === "writable"); - - const controller = $getByIdDirectPrivate(stream, "controller"); - $assert(controller !== undefined); - - $putByIdDirectPrivate(stream, "state", "erroring"); - $putByIdDirectPrivate(stream, "storedError", reason); - - const writer = $getByIdDirectPrivate(stream, "writer"); - if (writer !== undefined) $writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason); - - if (!$writableStreamHasOperationMarkedInFlight(stream) && $getByIdDirectPrivate(controller, "started") === 1) - $writableStreamFinishErroring(stream); -} - -export function writableStreamUpdateBackpressure(stream, backpressure) { - $assert($getByIdDirectPrivate(stream, "state") === "writable"); - $assert(!$writableStreamCloseQueuedOrInFlight(stream)); - - const writer = $getByIdDirectPrivate(stream, "writer"); - if (writer !== undefined && backpressure !== $getByIdDirectPrivate(stream, "backpressure")) { - if (backpressure) $putByIdDirectPrivate(writer, "readyPromise", $newPromiseCapability(Promise)); - else $getByIdDirectPrivate(writer, "readyPromise").$resolve.$call(); - } - $putByIdDirectPrivate(stream, "backpressure", backpressure); -} - -export function writableStreamDefaultWriterAbort(writer, reason) { - const stream = $getByIdDirectPrivate(writer, "stream"); - $assert(stream !== undefined); - return $writableStreamAbort(stream, reason); -} - -export function writableStreamDefaultWriterClose(writer) { - const stream = $getByIdDirectPrivate(writer, "stream"); - $assert(stream !== undefined); - return $writableStreamClose(stream); -} - -export function writableStreamDefaultWriterCloseWithErrorPropagation(writer) { - const stream = $getByIdDirectPrivate(writer, "stream"); - $assert(stream !== undefined); - - const state = $getByIdDirectPrivate(stream, "state"); - - if ($writableStreamCloseQueuedOrInFlight(stream) || state === "closed") return Promise.$resolve(); - - if (state === "errored") return Promise.$reject($getByIdDirectPrivate(stream, "storedError")); - - $assert(state === "writable" || state === "erroring"); - return $writableStreamDefaultWriterClose(writer); -} - -export function writableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) { - let closedPromiseCapability = $getByIdDirectPrivate(writer, "closedPromise"); - let closedPromise = closedPromiseCapability.$promise; - - if (($getPromiseInternalField(closedPromise, $promiseFieldFlags) & $promiseStateMask) !== $promiseStatePending) { - closedPromiseCapability = $newPromiseCapability(Promise); - closedPromise = closedPromiseCapability.$promise; - $putByIdDirectPrivate(writer, "closedPromise", closedPromiseCapability); - } - - closedPromiseCapability.$reject.$call(undefined, error); - $markPromiseAsHandled(closedPromise); -} - -export function writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) { - let readyPromiseCapability = $getByIdDirectPrivate(writer, "readyPromise"); - let readyPromise = readyPromiseCapability.$promise; - - if (($getPromiseInternalField(readyPromise, $promiseFieldFlags) & $promiseStateMask) !== $promiseStatePending) { - readyPromiseCapability = $newPromiseCapability(Promise); - readyPromise = readyPromiseCapability.$promise; - $putByIdDirectPrivate(writer, "readyPromise", readyPromiseCapability); - } - - readyPromiseCapability.$reject.$call(undefined, error); - $markPromiseAsHandled(readyPromise); -} - -export function writableStreamDefaultWriterGetDesiredSize(writer) { - const stream = $getByIdDirectPrivate(writer, "stream"); - $assert(stream !== undefined); - - const state = $getByIdDirectPrivate(stream, "state"); - - if (state === "errored" || state === "erroring") return null; - - if (state === "closed") return 0; - - return $writableStreamDefaultControllerGetDesiredSize($getByIdDirectPrivate(stream, "controller")); -} - -export function writableStreamDefaultWriterRelease(writer) { - const stream = $getByIdDirectPrivate(writer, "stream"); - $assert(stream !== undefined); - $assert($getByIdDirectPrivate(stream, "writer") === writer); - - const releasedError = $makeTypeError("writableStreamDefaultWriterRelease"); - - $writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError); - $writableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError); - - $putByIdDirectPrivate(stream, "writer", undefined); - $putByIdDirectPrivate(writer, "stream", undefined); -} - -export function writableStreamDefaultWriterWrite(writer, chunk) { - const stream = $getByIdDirectPrivate(writer, "stream"); - $assert(stream !== undefined); - - const controller = $getByIdDirectPrivate(stream, "controller"); - $assert(controller !== undefined); - const chunkSize = $writableStreamDefaultControllerGetChunkSize(controller, chunk); - - if (stream !== $getByIdDirectPrivate(writer, "stream")) - return Promise.$reject($makeTypeError("writer is not stream's writer")); - - const state = $getByIdDirectPrivate(stream, "state"); - if (state === "errored") return Promise.$reject($getByIdDirectPrivate(stream, "storedError")); - - if ($writableStreamCloseQueuedOrInFlight(stream) || state === "closed") - return Promise.$reject($makeTypeError("stream is closing or closed")); - - if ($writableStreamCloseQueuedOrInFlight(stream) || state === "closed") - return Promise.$reject($makeTypeError("stream is closing or closed")); - - if (state === "erroring") return Promise.$reject($getByIdDirectPrivate(stream, "storedError")); - - $assert(state === "writable"); - - const promise = $writableStreamAddWriteRequest(stream); - $writableStreamDefaultControllerWrite(controller, chunk, chunkSize); - return promise; -} - -export function setUpWritableStreamDefaultController( - stream, - controller, - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - highWaterMark, - sizeAlgorithm, -) { - $assert($isWritableStream(stream)); - $assert($getByIdDirectPrivate(stream, "controller") === undefined); - - $putByIdDirectPrivate(controller, "stream", stream); - $putByIdDirectPrivate(stream, "controller", controller); - - $resetQueue($getByIdDirectPrivate(controller, "queue")); - - $putByIdDirectPrivate(controller, "started", -1); - $putByIdDirectPrivate(controller, "startAlgorithm", startAlgorithm); - $putByIdDirectPrivate(controller, "strategySizeAlgorithm", sizeAlgorithm); - $putByIdDirectPrivate(controller, "strategyHWM", highWaterMark); - $putByIdDirectPrivate(controller, "writeAlgorithm", writeAlgorithm); - $putByIdDirectPrivate(controller, "closeAlgorithm", closeAlgorithm); - $putByIdDirectPrivate(controller, "abortAlgorithm", abortAlgorithm); - - const backpressure = $writableStreamDefaultControllerGetBackpressure(controller); - $writableStreamUpdateBackpressure(stream, backpressure); - - $writableStreamDefaultControllerStart(controller); -} - -export function writableStreamDefaultControllerStart(controller) { - if ($getByIdDirectPrivate(controller, "started") !== -1) return; - - $putByIdDirectPrivate(controller, "started", 0); - - const startAlgorithm = $getByIdDirectPrivate(controller, "startAlgorithm"); - $putByIdDirectPrivate(controller, "startAlgorithm", undefined); - const stream = $getByIdDirectPrivate(controller, "stream"); - return Promise.$resolve(startAlgorithm.$call()).$then( - () => { - const state = $getByIdDirectPrivate(stream, "state"); - $assert(state === "writable" || state === "erroring"); - $putByIdDirectPrivate(controller, "started", 1); - $writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); - }, - error => { - const state = $getByIdDirectPrivate(stream, "state"); - $assert(state === "writable" || state === "erroring"); - $putByIdDirectPrivate(controller, "started", 1); - $writableStreamDealWithRejection(stream, error); - }, - ); -} - -export function setUpWritableStreamDefaultControllerFromUnderlyingSink( - stream, - underlyingSink, - underlyingSinkDict, - highWaterMark, - sizeAlgorithm, -) { - const controller = new $WritableStreamDefaultController(); - - let startAlgorithm = () => {}; - let writeAlgorithm = () => { - return Promise.$resolve(); - }; - let closeAlgorithm = () => { - return Promise.$resolve(); - }; - let abortAlgorithm = () => { - return Promise.$resolve(); - }; - - if ("start" in underlyingSinkDict) { - const startMethod = underlyingSinkDict["start"]; - startAlgorithm = () => $promiseInvokeOrNoopMethodNoCatch(underlyingSink, startMethod, [controller]); - } - if ("write" in underlyingSinkDict) { - const writeMethod = underlyingSinkDict["write"]; - writeAlgorithm = chunk => $promiseInvokeOrNoopMethod(underlyingSink, writeMethod, [chunk, controller]); - } - if ("close" in underlyingSinkDict) { - const closeMethod = underlyingSinkDict["close"]; - closeAlgorithm = () => $promiseInvokeOrNoopMethod(underlyingSink, closeMethod, []); - } - if ("abort" in underlyingSinkDict) { - const abortMethod = underlyingSinkDict["abort"]; - abortAlgorithm = reason => $promiseInvokeOrNoopMethod(underlyingSink, abortMethod, [reason]); - } - - $setUpWritableStreamDefaultController( - stream, - controller, - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - highWaterMark, - sizeAlgorithm, - ); -} - -export function writableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { - const stream = $getByIdDirectPrivate(controller, "stream"); - - if ($getByIdDirectPrivate(controller, "started") !== 1) return; - - $assert(stream !== undefined); - if ($getByIdDirectPrivate(stream, "inFlightWriteRequest") !== undefined) return; - - const state = $getByIdDirectPrivate(stream, "state"); - $assert(state !== "closed" || state !== "errored"); - if (state === "erroring") { - $writableStreamFinishErroring(stream); - return; - } - - const queue = $getByIdDirectPrivate(controller, "queue"); - - if (queue.content?.isEmpty() ?? false) return; - - const value = $peekQueueValue(queue); - if (value === $isCloseSentinel) $writableStreamDefaultControllerProcessClose(controller); - else $writableStreamDefaultControllerProcessWrite(controller, value); -} - -export function isCloseSentinel() {} - -export function writableStreamDefaultControllerClearAlgorithms(controller) { - $putByIdDirectPrivate(controller, "writeAlgorithm", undefined); - $putByIdDirectPrivate(controller, "closeAlgorithm", undefined); - $putByIdDirectPrivate(controller, "abortAlgorithm", undefined); - $putByIdDirectPrivate(controller, "strategySizeAlgorithm", undefined); -} - -export function writableStreamDefaultControllerClose(controller) { - $enqueueValueWithSize($getByIdDirectPrivate(controller, "queue"), $isCloseSentinel, 0); - $writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); -} - -export function writableStreamDefaultControllerError(controller, error) { - const stream = $getByIdDirectPrivate(controller, "stream"); - $assert(stream !== undefined); - $assert($getByIdDirectPrivate(stream, "state") === "writable"); - - $writableStreamDefaultControllerClearAlgorithms(controller); - $writableStreamStartErroring(stream, error); -} - -export function writableStreamDefaultControllerErrorIfNeeded(controller, error) { - const stream = $getByIdDirectPrivate(controller, "stream"); - if ($getByIdDirectPrivate(stream, "state") === "writable") $writableStreamDefaultControllerError(controller, error); -} - -export function writableStreamDefaultControllerGetBackpressure(controller) { - const desiredSize = $writableStreamDefaultControllerGetDesiredSize(controller); - return desiredSize <= 0; -} - -export function writableStreamDefaultControllerGetChunkSize(controller, chunk) { - try { - return $getByIdDirectPrivate(controller, "strategySizeAlgorithm").$call(undefined, chunk); - } catch (e) { - $writableStreamDefaultControllerErrorIfNeeded(controller, e); - return 1; - } -} - -export function writableStreamDefaultControllerGetDesiredSize(controller) { - return $getByIdDirectPrivate(controller, "strategyHWM") - $getByIdDirectPrivate(controller, "queue").size; -} - -export function writableStreamDefaultControllerProcessClose(controller) { - const stream = $getByIdDirectPrivate(controller, "stream"); - - $writableStreamMarkCloseRequestInFlight(stream); - $dequeueValue($getByIdDirectPrivate(controller, "queue")); - - $assert($getByIdDirectPrivate(controller, "queue").content?.isEmpty()); - - const sinkClosePromise = $getByIdDirectPrivate(controller, "closeAlgorithm").$call(); - $writableStreamDefaultControllerClearAlgorithms(controller); - - sinkClosePromise.$then( - () => { - $writableStreamFinishInFlightClose(stream); - }, - reason => { - $writableStreamFinishInFlightCloseWithError(stream, reason); - }, - ); -} - -export function writableStreamDefaultControllerProcessWrite(controller, chunk) { - const stream = $getByIdDirectPrivate(controller, "stream"); - - $writableStreamMarkFirstWriteRequestInFlight(stream); - - const sinkWritePromise = $getByIdDirectPrivate(controller, "writeAlgorithm").$call(undefined, chunk); - - sinkWritePromise.$then( - () => { - $writableStreamFinishInFlightWrite(stream); - const state = $getByIdDirectPrivate(stream, "state"); - $assert(state === "writable" || state === "erroring"); - - $dequeueValue($getByIdDirectPrivate(controller, "queue")); - if (!$writableStreamCloseQueuedOrInFlight(stream) && state === "writable") { - const backpressure = $writableStreamDefaultControllerGetBackpressure(controller); - $writableStreamUpdateBackpressure(stream, backpressure); - } - $writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); - }, - reason => { - const state = $getByIdDirectPrivate(stream, "state"); - if (state === "writable") $writableStreamDefaultControllerClearAlgorithms(controller); - - $writableStreamFinishInFlightWriteWithError(stream, reason); - }, - ); -} - -export function writableStreamDefaultControllerWrite(controller, chunk, chunkSize) { - try { - $enqueueValueWithSize($getByIdDirectPrivate(controller, "queue"), chunk, chunkSize); - - const stream = $getByIdDirectPrivate(controller, "stream"); - - const state = $getByIdDirectPrivate(stream, "state"); - if (!$writableStreamCloseQueuedOrInFlight(stream) && state === "writable") { - const backpressure = $writableStreamDefaultControllerGetBackpressure(controller); - $writableStreamUpdateBackpressure(stream, backpressure); - } - $writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); - } catch (e) { - $writableStreamDefaultControllerErrorIfNeeded(controller, e); - } -} |