diff options
Diffstat (limited to 'ext/web')
-rw-r--r-- | ext/web/00_infra.js | 605 | ||||
-rw-r--r-- | ext/web/01_dom_exception.js | 373 | ||||
-rw-r--r-- | ext/web/01_mimesniff.js | 420 | ||||
-rw-r--r-- | ext/web/02_event.js | 2559 | ||||
-rw-r--r-- | ext/web/02_structured_clone.js | 239 | ||||
-rw-r--r-- | ext/web/02_timers.js | 691 | ||||
-rw-r--r-- | ext/web/03_abort_signal.js | 335 | ||||
-rw-r--r-- | ext/web/04_global_interfaces.js | 124 | ||||
-rw-r--r-- | ext/web/05_base64.js | 110 | ||||
-rw-r--r-- | ext/web/06_streams.js | 11235 | ||||
-rw-r--r-- | ext/web/08_text_encoding.js | 777 | ||||
-rw-r--r-- | ext/web/09_file.js | 1108 | ||||
-rw-r--r-- | ext/web/10_filereader.js | 845 | ||||
-rw-r--r-- | ext/web/11_blob_url.js | 84 | ||||
-rw-r--r-- | ext/web/12_location.js | 729 | ||||
-rw-r--r-- | ext/web/13_message_port.js | 609 | ||||
-rw-r--r-- | ext/web/14_compression.js | 227 | ||||
-rw-r--r-- | ext/web/15_performance.js | 1032 | ||||
-rw-r--r-- | ext/web/benches/encoding.rs | 9 | ||||
-rw-r--r-- | ext/web/benches/timers_ops.rs | 7 | ||||
-rw-r--r-- | ext/web/internal.d.ts | 201 | ||||
-rw-r--r-- | ext/web/lib.rs | 2 |
22 files changed, 11140 insertions, 11181 deletions
diff --git a/ext/web/00_infra.js b/ext/web/00_infra.js index 3f3f98165..c44b124c9 100644 --- a/ext/web/00_infra.js +++ b/ext/web/00_infra.js @@ -6,334 +6,331 @@ /// <reference path="../web/internal.d.ts" /> /// <reference path="../web/lib.deno_web.d.ts" /> -"use strict"; +const core = globalThis.Deno.core; +const ops = core.ops; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypeJoin, + ArrayPrototypeMap, + Error, + JSONStringify, + NumberPrototypeToString, + RegExp, + SafeArrayIterator, + String, + StringPrototypeCharAt, + StringPrototypeCharCodeAt, + StringPrototypeMatch, + StringPrototypePadStart, + StringPrototypeReplace, + StringPrototypeSlice, + StringPrototypeSubstring, + StringPrototypeToLowerCase, + StringPrototypeToUpperCase, + TypeError, +} = primordials; -((window) => { - const core = Deno.core; - const ops = core.ops; - const { - ArrayPrototypeJoin, - ArrayPrototypeMap, - Error, - JSONStringify, - NumberPrototypeToString, - RegExp, - SafeArrayIterator, - String, - StringPrototypeCharAt, - StringPrototypeCharCodeAt, - StringPrototypeMatch, - StringPrototypePadStart, - StringPrototypeReplace, - StringPrototypeSlice, - StringPrototypeSubstring, - StringPrototypeToLowerCase, - StringPrototypeToUpperCase, - TypeError, - } = window.__bootstrap.primordials; +const ASCII_DIGIT = ["\u0030-\u0039"]; +const ASCII_UPPER_ALPHA = ["\u0041-\u005A"]; +const ASCII_LOWER_ALPHA = ["\u0061-\u007A"]; +const ASCII_ALPHA = [ + ...new SafeArrayIterator(ASCII_UPPER_ALPHA), + ...new SafeArrayIterator(ASCII_LOWER_ALPHA), +]; +const ASCII_ALPHANUMERIC = [ + ...new SafeArrayIterator(ASCII_DIGIT), + ...new SafeArrayIterator(ASCII_ALPHA), +]; - const ASCII_DIGIT = ["\u0030-\u0039"]; - const ASCII_UPPER_ALPHA = ["\u0041-\u005A"]; - const ASCII_LOWER_ALPHA = ["\u0061-\u007A"]; - const ASCII_ALPHA = [ - ...new SafeArrayIterator(ASCII_UPPER_ALPHA), - ...new SafeArrayIterator(ASCII_LOWER_ALPHA), - ]; - const ASCII_ALPHANUMERIC = [ - ...new SafeArrayIterator(ASCII_DIGIT), - ...new SafeArrayIterator(ASCII_ALPHA), - ]; +const HTTP_TAB_OR_SPACE = ["\u0009", "\u0020"]; +const HTTP_WHITESPACE = [ + "\u000A", + "\u000D", + ...new SafeArrayIterator(HTTP_TAB_OR_SPACE), +]; - const HTTP_TAB_OR_SPACE = ["\u0009", "\u0020"]; - const HTTP_WHITESPACE = [ - "\u000A", - "\u000D", - ...new SafeArrayIterator(HTTP_TAB_OR_SPACE), - ]; +const HTTP_TOKEN_CODE_POINT = [ + "\u0021", + "\u0023", + "\u0024", + "\u0025", + "\u0026", + "\u0027", + "\u002A", + "\u002B", + "\u002D", + "\u002E", + "\u005E", + "\u005F", + "\u0060", + "\u007C", + "\u007E", + ...new SafeArrayIterator(ASCII_ALPHANUMERIC), +]; +const HTTP_TOKEN_CODE_POINT_RE = new RegExp( + `^[${regexMatcher(HTTP_TOKEN_CODE_POINT)}]+$`, +); +const HTTP_QUOTED_STRING_TOKEN_POINT = [ + "\u0009", + "\u0020-\u007E", + "\u0080-\u00FF", +]; +const HTTP_QUOTED_STRING_TOKEN_POINT_RE = new RegExp( + `^[${regexMatcher(HTTP_QUOTED_STRING_TOKEN_POINT)}]+$`, +); +const HTTP_TAB_OR_SPACE_MATCHER = regexMatcher(HTTP_TAB_OR_SPACE); +const HTTP_TAB_OR_SPACE_PREFIX_RE = new RegExp( + `^[${HTTP_TAB_OR_SPACE_MATCHER}]+`, + "g", +); +const HTTP_TAB_OR_SPACE_SUFFIX_RE = new RegExp( + `[${HTTP_TAB_OR_SPACE_MATCHER}]+$`, + "g", +); +const HTTP_WHITESPACE_MATCHER = regexMatcher(HTTP_WHITESPACE); +const HTTP_BETWEEN_WHITESPACE = new RegExp( + `^[${HTTP_WHITESPACE_MATCHER}]*(.*?)[${HTTP_WHITESPACE_MATCHER}]*$`, +); +const HTTP_WHITESPACE_PREFIX_RE = new RegExp( + `^[${HTTP_WHITESPACE_MATCHER}]+`, + "g", +); +const HTTP_WHITESPACE_SUFFIX_RE = new RegExp( + `[${HTTP_WHITESPACE_MATCHER}]+$`, + "g", +); - const HTTP_TOKEN_CODE_POINT = [ - "\u0021", - "\u0023", - "\u0024", - "\u0025", - "\u0026", - "\u0027", - "\u002A", - "\u002B", - "\u002D", - "\u002E", - "\u005E", - "\u005F", - "\u0060", - "\u007C", - "\u007E", - ...new SafeArrayIterator(ASCII_ALPHANUMERIC), - ]; - const HTTP_TOKEN_CODE_POINT_RE = new RegExp( - `^[${regexMatcher(HTTP_TOKEN_CODE_POINT)}]+$`, - ); - const HTTP_QUOTED_STRING_TOKEN_POINT = [ - "\u0009", - "\u0020-\u007E", - "\u0080-\u00FF", - ]; - const HTTP_QUOTED_STRING_TOKEN_POINT_RE = new RegExp( - `^[${regexMatcher(HTTP_QUOTED_STRING_TOKEN_POINT)}]+$`, - ); - const HTTP_TAB_OR_SPACE_MATCHER = regexMatcher(HTTP_TAB_OR_SPACE); - const HTTP_TAB_OR_SPACE_PREFIX_RE = new RegExp( - `^[${HTTP_TAB_OR_SPACE_MATCHER}]+`, - "g", - ); - const HTTP_TAB_OR_SPACE_SUFFIX_RE = new RegExp( - `[${HTTP_TAB_OR_SPACE_MATCHER}]+$`, - "g", - ); - const HTTP_WHITESPACE_MATCHER = regexMatcher(HTTP_WHITESPACE); - const HTTP_BETWEEN_WHITESPACE = new RegExp( - `^[${HTTP_WHITESPACE_MATCHER}]*(.*?)[${HTTP_WHITESPACE_MATCHER}]*$`, - ); - const HTTP_WHITESPACE_PREFIX_RE = new RegExp( - `^[${HTTP_WHITESPACE_MATCHER}]+`, - "g", - ); - const HTTP_WHITESPACE_SUFFIX_RE = new RegExp( - `[${HTTP_WHITESPACE_MATCHER}]+$`, - "g", +/** + * Turn a string of chars into a regex safe matcher. + * @param {string[]} chars + * @returns {string} + */ +function regexMatcher(chars) { + const matchers = ArrayPrototypeMap(chars, (char) => { + if (char.length === 1) { + const a = StringPrototypePadStart( + NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16), + 4, + "0", + ); + return `\\u${a}`; + } else if (char.length === 3 && char[1] === "-") { + const a = StringPrototypePadStart( + NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16), + 4, + "0", + ); + const b = StringPrototypePadStart( + NumberPrototypeToString(StringPrototypeCharCodeAt(char, 2), 16), + 4, + "0", + ); + return `\\u${a}-\\u${b}`; + } else { + throw TypeError("unreachable"); + } + }); + return ArrayPrototypeJoin(matchers, ""); +} + +/** + * https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points + * @param {string} input + * @param {number} position + * @param {(char: string) => boolean} condition + * @returns {{result: string, position: number}} + */ +function collectSequenceOfCodepoints(input, position, condition) { + const start = position; + for ( + let c = StringPrototypeCharAt(input, position); + position < input.length && condition(c); + c = StringPrototypeCharAt(input, ++position) ); + return { result: StringPrototypeSlice(input, start, position), position }; +} - /** - * Turn a string of chars into a regex safe matcher. - * @param {string[]} chars - * @returns {string} - */ - function regexMatcher(chars) { - const matchers = ArrayPrototypeMap(chars, (char) => { - if (char.length === 1) { - const a = StringPrototypePadStart( - NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16), - 4, - "0", - ); - return `\\u${a}`; - } else if (char.length === 3 && char[1] === "-") { - const a = StringPrototypePadStart( - NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16), - 4, - "0", - ); - const b = StringPrototypePadStart( - NumberPrototypeToString(StringPrototypeCharCodeAt(char, 2), 16), - 4, - "0", - ); - return `\\u${a}-\\u${b}`; - } else { - throw TypeError("unreachable"); - } - }); - return ArrayPrototypeJoin(matchers, ""); - } +/** + * @param {string} s + * @returns {string} + */ +function byteUpperCase(s) { + return StringPrototypeReplace( + String(s), + /[a-z]/g, + function byteUpperCaseReplace(c) { + return StringPrototypeToUpperCase(c); + }, + ); +} - /** - * https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points - * @param {string} input - * @param {number} position - * @param {(char: string) => boolean} condition - * @returns {{result: string, position: number}} - */ - function collectSequenceOfCodepoints(input, position, condition) { - const start = position; - for ( - let c = StringPrototypeCharAt(input, position); - position < input.length && condition(c); - c = StringPrototypeCharAt(input, ++position) - ); - return { result: StringPrototypeSlice(input, start, position), position }; - } +/** + * @param {string} s + * @returns {string} + */ +function byteLowerCase(s) { + // NOTE: correct since all callers convert to ByteString first + // TODO(@AaronO): maybe prefer a ByteString_Lower webidl converter + return StringPrototypeToLowerCase(s); +} - /** - * @param {string} s - * @returns {string} - */ - function byteUpperCase(s) { - return StringPrototypeReplace( - String(s), - /[a-z]/g, - function byteUpperCaseReplace(c) { - return StringPrototypeToUpperCase(c); - }, +/** + * https://fetch.spec.whatwg.org/#collect-an-http-quoted-string + * @param {string} input + * @param {number} position + * @param {boolean} extractValue + * @returns {{result: string, position: number}} + */ +function collectHttpQuotedString(input, position, extractValue) { + // 1. + const positionStart = position; + // 2. + let value = ""; + // 3. + if (input[position] !== "\u0022") throw new TypeError('must be "'); + // 4. + position++; + // 5. + while (true) { + // 5.1. + const res = collectSequenceOfCodepoints( + input, + position, + (c) => c !== "\u0022" && c !== "\u005C", ); - } - - /** - * @param {string} s - * @returns {string} - */ - function byteLowerCase(s) { - // NOTE: correct since all callers convert to ByteString first - // TODO(@AaronO): maybe prefer a ByteString_Lower webidl converter - return StringPrototypeToLowerCase(s); - } - - /** - * https://fetch.spec.whatwg.org/#collect-an-http-quoted-string - * @param {string} input - * @param {number} position - * @param {boolean} extractValue - * @returns {{result: string, position: number}} - */ - function collectHttpQuotedString(input, position, extractValue) { - // 1. - const positionStart = position; - // 2. - let value = ""; - // 3. - if (input[position] !== "\u0022") throw new TypeError('must be "'); - // 4. + value += res.result; + position = res.position; + // 5.2. + if (position >= input.length) break; + // 5.3. + const quoteOrBackslash = input[position]; + // 5.4. position++; - // 5. - while (true) { - // 5.1. - const res = collectSequenceOfCodepoints( - input, - position, - (c) => c !== "\u0022" && c !== "\u005C", - ); - value += res.result; - position = res.position; - // 5.2. - if (position >= input.length) break; - // 5.3. - const quoteOrBackslash = input[position]; - // 5.4. - position++; - // 5.5. - if (quoteOrBackslash === "\u005C") { - // 5.5.1. - if (position >= input.length) { - value += "\u005C"; - break; - } - // 5.5.2. - value += input[position]; - // 5.5.3. - position++; - } else { // 5.6. - // 5.6.1 - if (quoteOrBackslash !== "\u0022") throw new TypeError('must be "'); - // 5.6.2 + // 5.5. + if (quoteOrBackslash === "\u005C") { + // 5.5.1. + if (position >= input.length) { + value += "\u005C"; break; } + // 5.5.2. + value += input[position]; + // 5.5.3. + position++; + } else { // 5.6. + // 5.6.1 + if (quoteOrBackslash !== "\u0022") throw new TypeError('must be "'); + // 5.6.2 + break; } - // 6. - if (extractValue) return { result: value, position }; - // 7. - return { - result: StringPrototypeSubstring(input, positionStart, position + 1), - position, - }; } + // 6. + if (extractValue) return { result: value, position }; + // 7. + return { + result: StringPrototypeSubstring(input, positionStart, position + 1), + position, + }; +} - /** - * @param {Uint8Array} data - * @returns {string} - */ - function forgivingBase64Encode(data) { - return ops.op_base64_encode(data); - } +/** + * @param {Uint8Array} data + * @returns {string} + */ +function forgivingBase64Encode(data) { + return ops.op_base64_encode(data); +} - /** - * @param {string} data - * @returns {Uint8Array} - */ - function forgivingBase64Decode(data) { - return ops.op_base64_decode(data); - } +/** + * @param {string} data + * @returns {Uint8Array} + */ +function forgivingBase64Decode(data) { + return ops.op_base64_decode(data); +} - /** - * @param {string} char - * @returns {boolean} - */ - function isHttpWhitespace(char) { - switch (char) { - case "\u0009": - case "\u000A": - case "\u000D": - case "\u0020": - return true; - default: - return false; - } +/** + * @param {string} char + * @returns {boolean} + */ +function isHttpWhitespace(char) { + switch (char) { + case "\u0009": + case "\u000A": + case "\u000D": + case "\u0020": + return true; + default: + return false; } +} - /** - * @param {string} s - * @returns {string} - */ - function httpTrim(s) { - if (!isHttpWhitespace(s[0]) && !isHttpWhitespace(s[s.length - 1])) { - return s; - } - return StringPrototypeMatch(s, HTTP_BETWEEN_WHITESPACE)?.[1] ?? ""; +/** + * @param {string} s + * @returns {string} + */ +function httpTrim(s) { + if (!isHttpWhitespace(s[0]) && !isHttpWhitespace(s[s.length - 1])) { + return s; } + return StringPrototypeMatch(s, HTTP_BETWEEN_WHITESPACE)?.[1] ?? ""; +} - class AssertionError extends Error { - constructor(msg) { - super(msg); - this.name = "AssertionError"; - } +class AssertionError extends Error { + constructor(msg) { + super(msg); + this.name = "AssertionError"; } +} - /** - * @param {unknown} cond - * @param {string=} msg - * @returns {asserts cond} - */ - function assert(cond, msg = "Assertion failed.") { - if (!cond) { - throw new AssertionError(msg); - } +/** + * @param {unknown} cond + * @param {string=} msg + * @returns {asserts cond} + */ +function assert(cond, msg = "Assertion failed.") { + if (!cond) { + throw new AssertionError(msg); } +} - /** - * @param {unknown} value - * @returns {string} - */ - function serializeJSValueToJSONString(value) { - const result = JSONStringify(value); - if (result === undefined) { - throw new TypeError("Value is not JSON serializable."); - } - return result; +/** + * @param {unknown} value + * @returns {string} + */ +function serializeJSValueToJSONString(value) { + const result = JSONStringify(value); + if (result === undefined) { + throw new TypeError("Value is not JSON serializable."); } + return result; +} - window.__bootstrap.infra = { - collectSequenceOfCodepoints, - ASCII_DIGIT, - ASCII_UPPER_ALPHA, - ASCII_LOWER_ALPHA, - ASCII_ALPHA, - ASCII_ALPHANUMERIC, - HTTP_TAB_OR_SPACE, - HTTP_WHITESPACE, - HTTP_TOKEN_CODE_POINT, - HTTP_TOKEN_CODE_POINT_RE, - HTTP_QUOTED_STRING_TOKEN_POINT, - HTTP_QUOTED_STRING_TOKEN_POINT_RE, - HTTP_TAB_OR_SPACE_PREFIX_RE, - HTTP_TAB_OR_SPACE_SUFFIX_RE, - HTTP_WHITESPACE_PREFIX_RE, - HTTP_WHITESPACE_SUFFIX_RE, - httpTrim, - regexMatcher, - byteUpperCase, - byteLowerCase, - collectHttpQuotedString, - forgivingBase64Encode, - forgivingBase64Decode, - AssertionError, - assert, - serializeJSValueToJSONString, - }; -})(globalThis); +export { + ASCII_ALPHA, + ASCII_ALPHANUMERIC, + ASCII_DIGIT, + ASCII_LOWER_ALPHA, + ASCII_UPPER_ALPHA, + assert, + AssertionError, + byteLowerCase, + byteUpperCase, + collectHttpQuotedString, + collectSequenceOfCodepoints, + forgivingBase64Decode, + forgivingBase64Encode, + HTTP_QUOTED_STRING_TOKEN_POINT, + HTTP_QUOTED_STRING_TOKEN_POINT_RE, + HTTP_TAB_OR_SPACE, + HTTP_TAB_OR_SPACE_PREFIX_RE, + HTTP_TAB_OR_SPACE_SUFFIX_RE, + HTTP_TOKEN_CODE_POINT, + HTTP_TOKEN_CODE_POINT_RE, + HTTP_WHITESPACE, + HTTP_WHITESPACE_PREFIX_RE, + HTTP_WHITESPACE_SUFFIX_RE, + httpTrim, + regexMatcher, + serializeJSValueToJSONString, +}; diff --git a/ext/web/01_dom_exception.js b/ext/web/01_dom_exception.js index a4556c03c..cbec9ca22 100644 --- a/ext/web/01_dom_exception.js +++ b/ext/web/01_dom_exception.js @@ -7,197 +7,194 @@ /// <reference path="../web/internal.d.ts" /> /// <reference path="../web/lib.deno_web.d.ts" /> -"use strict"; - -((window) => { - const { - ArrayPrototypeSlice, - Error, - ErrorPrototype, - ObjectDefineProperty, - ObjectCreate, - ObjectEntries, - ObjectPrototypeIsPrototypeOf, - ObjectSetPrototypeOf, - Symbol, - SymbolFor, - } = window.__bootstrap.primordials; - const webidl = window.__bootstrap.webidl; - const consoleInternal = window.__bootstrap.console; - - const _name = Symbol("name"); - const _message = Symbol("message"); - const _code = Symbol("code"); - - // Defined in WebIDL 4.3. - // https://webidl.spec.whatwg.org/#idl-DOMException - const INDEX_SIZE_ERR = 1; - const DOMSTRING_SIZE_ERR = 2; - const HIERARCHY_REQUEST_ERR = 3; - const WRONG_DOCUMENT_ERR = 4; - const INVALID_CHARACTER_ERR = 5; - const NO_DATA_ALLOWED_ERR = 6; - const NO_MODIFICATION_ALLOWED_ERR = 7; - const NOT_FOUND_ERR = 8; - const NOT_SUPPORTED_ERR = 9; - const INUSE_ATTRIBUTE_ERR = 10; - const INVALID_STATE_ERR = 11; - const SYNTAX_ERR = 12; - const INVALID_MODIFICATION_ERR = 13; - const NAMESPACE_ERR = 14; - const INVALID_ACCESS_ERR = 15; - const VALIDATION_ERR = 16; - const TYPE_MISMATCH_ERR = 17; - const SECURITY_ERR = 18; - const NETWORK_ERR = 19; - const ABORT_ERR = 20; - const URL_MISMATCH_ERR = 21; - const QUOTA_EXCEEDED_ERR = 22; - const TIMEOUT_ERR = 23; - const INVALID_NODE_TYPE_ERR = 24; - const DATA_CLONE_ERR = 25; - - // Defined in WebIDL 2.8.1. - // https://webidl.spec.whatwg.org/#dfn-error-names-table - /** @type {Record<string, number>} */ - // the prototype should be null, to prevent user code from looking - // up Object.prototype properties, such as "toString" - const nameToCodeMapping = ObjectCreate(null, { - IndexSizeError: { value: INDEX_SIZE_ERR }, - HierarchyRequestError: { value: HIERARCHY_REQUEST_ERR }, - WrongDocumentError: { value: WRONG_DOCUMENT_ERR }, - InvalidCharacterError: { value: INVALID_CHARACTER_ERR }, - NoModificationAllowedError: { value: NO_MODIFICATION_ALLOWED_ERR }, - NotFoundError: { value: NOT_FOUND_ERR }, - NotSupportedError: { value: NOT_SUPPORTED_ERR }, - InUseAttributeError: { value: INUSE_ATTRIBUTE_ERR }, - InvalidStateError: { value: INVALID_STATE_ERR }, - SyntaxError: { value: SYNTAX_ERR }, - InvalidModificationError: { value: INVALID_MODIFICATION_ERR }, - NamespaceError: { value: NAMESPACE_ERR }, - InvalidAccessError: { value: INVALID_ACCESS_ERR }, - TypeMismatchError: { value: TYPE_MISMATCH_ERR }, - SecurityError: { value: SECURITY_ERR }, - NetworkError: { value: NETWORK_ERR }, - AbortError: { value: ABORT_ERR }, - URLMismatchError: { value: URL_MISMATCH_ERR }, - QuotaExceededError: { value: QUOTA_EXCEEDED_ERR }, - TimeoutError: { value: TIMEOUT_ERR }, - InvalidNodeTypeError: { value: INVALID_NODE_TYPE_ERR }, - DataCloneError: { value: DATA_CLONE_ERR }, - }); - - // Defined in WebIDL 4.3. - // https://webidl.spec.whatwg.org/#idl-DOMException - class DOMException { - [_message]; - [_name]; - [_code]; - - // https://webidl.spec.whatwg.org/#dom-domexception-domexception - constructor(message = "", name = "Error") { - message = webidl.converters.DOMString(message, { - prefix: "Failed to construct 'DOMException'", - context: "Argument 1", - }); - name = webidl.converters.DOMString(name, { - prefix: "Failed to construct 'DOMException'", - context: "Argument 2", - }); - const code = nameToCodeMapping[name] ?? 0; - - this[_message] = message; - this[_name] = name; - this[_code] = code; - this[webidl.brand] = webidl.brand; - - const error = new Error(message); - error.name = "DOMException"; - ObjectDefineProperty(this, "stack", { - value: error.stack, - writable: true, - configurable: true, - }); - - // `DOMException` isn't a native error, so `Error.prepareStackTrace()` is - // not called when accessing `.stack`, meaning our structured stack trace - // hack doesn't apply. This patches it in. - ObjectDefineProperty(this, "__callSiteEvals", { - value: ArrayPrototypeSlice(error.__callSiteEvals, 1), - configurable: true, - }); - } - - get message() { - webidl.assertBranded(this, DOMExceptionPrototype); - return this[_message]; - } - - get name() { - webidl.assertBranded(this, DOMExceptionPrototype); - return this[_name]; - } +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypeSlice, + Error, + ErrorPrototype, + ObjectDefineProperty, + ObjectCreate, + ObjectEntries, + ObjectPrototypeIsPrototypeOf, + ObjectSetPrototypeOf, + Symbol, + SymbolFor, +} = primordials; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { createFilteredInspectProxy } from "internal:ext/console/02_console.js"; + +const _name = Symbol("name"); +const _message = Symbol("message"); +const _code = Symbol("code"); + +// Defined in WebIDL 4.3. +// https://webidl.spec.whatwg.org/#idl-DOMException +const INDEX_SIZE_ERR = 1; +const DOMSTRING_SIZE_ERR = 2; +const HIERARCHY_REQUEST_ERR = 3; +const WRONG_DOCUMENT_ERR = 4; +const INVALID_CHARACTER_ERR = 5; +const NO_DATA_ALLOWED_ERR = 6; +const NO_MODIFICATION_ALLOWED_ERR = 7; +const NOT_FOUND_ERR = 8; +const NOT_SUPPORTED_ERR = 9; +const INUSE_ATTRIBUTE_ERR = 10; +const INVALID_STATE_ERR = 11; +const SYNTAX_ERR = 12; +const INVALID_MODIFICATION_ERR = 13; +const NAMESPACE_ERR = 14; +const INVALID_ACCESS_ERR = 15; +const VALIDATION_ERR = 16; +const TYPE_MISMATCH_ERR = 17; +const SECURITY_ERR = 18; +const NETWORK_ERR = 19; +const ABORT_ERR = 20; +const URL_MISMATCH_ERR = 21; +const QUOTA_EXCEEDED_ERR = 22; +const TIMEOUT_ERR = 23; +const INVALID_NODE_TYPE_ERR = 24; +const DATA_CLONE_ERR = 25; + +// Defined in WebIDL 2.8.1. +// https://webidl.spec.whatwg.org/#dfn-error-names-table +/** @type {Record<string, number>} */ +// the prototype should be null, to prevent user code from looking +// up Object.prototype properties, such as "toString" +const nameToCodeMapping = ObjectCreate(null, { + IndexSizeError: { value: INDEX_SIZE_ERR }, + HierarchyRequestError: { value: HIERARCHY_REQUEST_ERR }, + WrongDocumentError: { value: WRONG_DOCUMENT_ERR }, + InvalidCharacterError: { value: INVALID_CHARACTER_ERR }, + NoModificationAllowedError: { value: NO_MODIFICATION_ALLOWED_ERR }, + NotFoundError: { value: NOT_FOUND_ERR }, + NotSupportedError: { value: NOT_SUPPORTED_ERR }, + InUseAttributeError: { value: INUSE_ATTRIBUTE_ERR }, + InvalidStateError: { value: INVALID_STATE_ERR }, + SyntaxError: { value: SYNTAX_ERR }, + InvalidModificationError: { value: INVALID_MODIFICATION_ERR }, + NamespaceError: { value: NAMESPACE_ERR }, + InvalidAccessError: { value: INVALID_ACCESS_ERR }, + TypeMismatchError: { value: TYPE_MISMATCH_ERR }, + SecurityError: { value: SECURITY_ERR }, + NetworkError: { value: NETWORK_ERR }, + AbortError: { value: ABORT_ERR }, + URLMismatchError: { value: URL_MISMATCH_ERR }, + QuotaExceededError: { value: QUOTA_EXCEEDED_ERR }, + TimeoutError: { value: TIMEOUT_ERR }, + InvalidNodeTypeError: { value: INVALID_NODE_TYPE_ERR }, + DataCloneError: { value: DATA_CLONE_ERR }, +}); + +// Defined in WebIDL 4.3. +// https://webidl.spec.whatwg.org/#idl-DOMException +class DOMException { + [_message]; + [_name]; + [_code]; + + // https://webidl.spec.whatwg.org/#dom-domexception-domexception + constructor(message = "", name = "Error") { + message = webidl.converters.DOMString(message, { + prefix: "Failed to construct 'DOMException'", + context: "Argument 1", + }); + name = webidl.converters.DOMString(name, { + prefix: "Failed to construct 'DOMException'", + context: "Argument 2", + }); + const code = nameToCodeMapping[name] ?? 0; + + this[_message] = message; + this[_name] = name; + this[_code] = code; + this[webidl.brand] = webidl.brand; + + const error = new Error(message); + error.name = "DOMException"; + ObjectDefineProperty(this, "stack", { + value: error.stack, + writable: true, + configurable: true, + }); + + // `DOMException` isn't a native error, so `Error.prepareStackTrace()` is + // not called when accessing `.stack`, meaning our structured stack trace + // hack doesn't apply. This patches it in. + ObjectDefineProperty(this, "__callSiteEvals", { + value: ArrayPrototypeSlice(error.__callSiteEvals, 1), + configurable: true, + }); + } - get code() { - webidl.assertBranded(this, DOMExceptionPrototype); - return this[_code]; - } + get message() { + webidl.assertBranded(this, DOMExceptionPrototype); + return this[_message]; + } - [SymbolFor("Deno.customInspect")](inspect) { - if (ObjectPrototypeIsPrototypeOf(DOMExceptionPrototype, this)) { - return `DOMException: ${this[_message]}`; - } else { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: false, - keys: [ - "message", - "name", - "code", - ], - })); - } - } + get name() { + webidl.assertBranded(this, DOMExceptionPrototype); + return this[_name]; } - ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype); - - webidl.configurePrototype(DOMException); - const DOMExceptionPrototype = DOMException.prototype; - - const entries = ObjectEntries({ - INDEX_SIZE_ERR, - DOMSTRING_SIZE_ERR, - HIERARCHY_REQUEST_ERR, - WRONG_DOCUMENT_ERR, - INVALID_CHARACTER_ERR, - NO_DATA_ALLOWED_ERR, - NO_MODIFICATION_ALLOWED_ERR, - NOT_FOUND_ERR, - NOT_SUPPORTED_ERR, - INUSE_ATTRIBUTE_ERR, - INVALID_STATE_ERR, - SYNTAX_ERR, - INVALID_MODIFICATION_ERR, - NAMESPACE_ERR, - INVALID_ACCESS_ERR, - VALIDATION_ERR, - TYPE_MISMATCH_ERR, - SECURITY_ERR, - NETWORK_ERR, - ABORT_ERR, - URL_MISMATCH_ERR, - QUOTA_EXCEEDED_ERR, - TIMEOUT_ERR, - INVALID_NODE_TYPE_ERR, - DATA_CLONE_ERR, - }); - for (let i = 0; i < entries.length; ++i) { - const { 0: key, 1: value } = entries[i]; - const desc = { value, enumerable: true }; - ObjectDefineProperty(DOMException, key, desc); - ObjectDefineProperty(DOMException.prototype, key, desc); + get code() { + webidl.assertBranded(this, DOMExceptionPrototype); + return this[_code]; } - window.__bootstrap.domException = { DOMException }; -})(this); + [SymbolFor("Deno.customInspect")](inspect) { + if (ObjectPrototypeIsPrototypeOf(DOMExceptionPrototype, this)) { + return `DOMException: ${this[_message]}`; + } else { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: false, + keys: [ + "message", + "name", + "code", + ], + })); + } + } +} + +ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype); + +webidl.configurePrototype(DOMException); +const DOMExceptionPrototype = DOMException.prototype; + +const entries = ObjectEntries({ + INDEX_SIZE_ERR, + DOMSTRING_SIZE_ERR, + HIERARCHY_REQUEST_ERR, + WRONG_DOCUMENT_ERR, + INVALID_CHARACTER_ERR, + NO_DATA_ALLOWED_ERR, + NO_MODIFICATION_ALLOWED_ERR, + NOT_FOUND_ERR, + NOT_SUPPORTED_ERR, + INUSE_ATTRIBUTE_ERR, + INVALID_STATE_ERR, + SYNTAX_ERR, + INVALID_MODIFICATION_ERR, + NAMESPACE_ERR, + INVALID_ACCESS_ERR, + VALIDATION_ERR, + TYPE_MISMATCH_ERR, + SECURITY_ERR, + NETWORK_ERR, + ABORT_ERR, + URL_MISMATCH_ERR, + QUOTA_EXCEEDED_ERR, + TIMEOUT_ERR, + INVALID_NODE_TYPE_ERR, + DATA_CLONE_ERR, +}); +for (let i = 0; i < entries.length; ++i) { + const { 0: key, 1: value } = entries[i]; + const desc = { value, enumerable: true }; + ObjectDefineProperty(DOMException, key, desc); + ObjectDefineProperty(DOMException.prototype, key, desc); +} + +export default DOMException; diff --git a/ext/web/01_mimesniff.js b/ext/web/01_mimesniff.js index 2d67d5f95..17d954eb4 100644 --- a/ext/web/01_mimesniff.js +++ b/ext/web/01_mimesniff.js @@ -6,255 +6,247 @@ /// <reference path="../web/internal.d.ts" /> /// <reference path="../web/lib.deno_web.d.ts" /> -"use strict"; - -((window) => { - const { - ArrayPrototypeIncludes, - Map, - MapPrototypeGet, - MapPrototypeHas, - MapPrototypeSet, - RegExpPrototypeTest, - SafeMapIterator, - StringPrototypeReplaceAll, - StringPrototypeToLowerCase, - } = window.__bootstrap.primordials; - const { - collectSequenceOfCodepoints, - HTTP_WHITESPACE, - HTTP_WHITESPACE_PREFIX_RE, - HTTP_WHITESPACE_SUFFIX_RE, - HTTP_QUOTED_STRING_TOKEN_POINT_RE, - HTTP_TOKEN_CODE_POINT_RE, - collectHttpQuotedString, - } = window.__bootstrap.infra; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypeIncludes, + Map, + MapPrototypeGet, + MapPrototypeHas, + MapPrototypeSet, + RegExpPrototypeTest, + SafeMapIterator, + StringPrototypeReplaceAll, + StringPrototypeToLowerCase, +} = primordials; +import { + collectHttpQuotedString, + collectSequenceOfCodepoints, + HTTP_QUOTED_STRING_TOKEN_POINT_RE, + HTTP_TOKEN_CODE_POINT_RE, + HTTP_WHITESPACE, + HTTP_WHITESPACE_PREFIX_RE, + HTTP_WHITESPACE_SUFFIX_RE, +} from "internal:ext/web/00_infra.js"; + +/** + * @typedef MimeType + * @property {string} type + * @property {string} subtype + * @property {Map<string,string>} parameters + */ + +/** + * @param {string} input + * @returns {MimeType | null} + */ +function parseMimeType(input) { + // 1. + input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_PREFIX_RE, ""); + input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_SUFFIX_RE, ""); + + // 2. + let position = 0; + const endOfInput = input.length; + + // 3. + const res1 = collectSequenceOfCodepoints( + input, + position, + (c) => c != "\u002F", + ); + const type = res1.result; + position = res1.position; + + // 4. + if (type === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, type)) { + return null; + } - /** - * @typedef MimeType - * @property {string} type - * @property {string} subtype - * @property {Map<string,string>} parameters - */ + // 5. + if (position >= endOfInput) return null; + + // 6. + position++; + + // 7. + const res2 = collectSequenceOfCodepoints( + input, + position, + (c) => c != "\u003B", + ); + let subtype = res2.result; + position = res2.position; + + // 8. + subtype = StringPrototypeReplaceAll(subtype, HTTP_WHITESPACE_SUFFIX_RE, ""); + + // 9. + if ( + subtype === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, subtype) + ) { + return null; + } - /** - * @param {string} input - * @returns {MimeType | null} - */ - function parseMimeType(input) { - // 1. - input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_PREFIX_RE, ""); - input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_SUFFIX_RE, ""); + // 10. + const mimeType = { + type: StringPrototypeToLowerCase(type), + subtype: StringPrototypeToLowerCase(subtype), + /** @type {Map<string, string>} */ + parameters: new Map(), + }; - // 2. - let position = 0; - const endOfInput = input.length; + // 11. + while (position < endOfInput) { + // 11.1. + position++; - // 3. + // 11.2. const res1 = collectSequenceOfCodepoints( input, position, - (c) => c != "\u002F", + (c) => ArrayPrototypeIncludes(HTTP_WHITESPACE, c), ); - const type = res1.result; position = res1.position; - // 4. - if (type === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, type)) { - return null; - } - - // 5. - if (position >= endOfInput) return null; - - // 6. - position++; - - // 7. + // 11.3. const res2 = collectSequenceOfCodepoints( input, position, - (c) => c != "\u003B", + (c) => c !== "\u003B" && c !== "\u003D", ); - let subtype = res2.result; + let parameterName = res2.result; position = res2.position; - // 8. - subtype = StringPrototypeReplaceAll(subtype, HTTP_WHITESPACE_SUFFIX_RE, ""); + // 11.4. + parameterName = StringPrototypeToLowerCase(parameterName); - // 9. - if ( - subtype === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, subtype) - ) { - return null; + // 11.5. + if (position < endOfInput) { + if (input[position] == "\u003B") continue; + position++; } - // 10. - const mimeType = { - type: StringPrototypeToLowerCase(type), - subtype: StringPrototypeToLowerCase(subtype), - /** @type {Map<string, string>} */ - parameters: new Map(), - }; + // 11.6. + if (position >= endOfInput) break; - // 11. - while (position < endOfInput) { - // 11.1. - position++; + // 11.7. + let parameterValue = null; - // 11.2. - const res1 = collectSequenceOfCodepoints( - input, - position, - (c) => ArrayPrototypeIncludes(HTTP_WHITESPACE, c), - ); - position = res1.position; + // 11.8. + if (input[position] === "\u0022") { + // 11.8.1. + const res = collectHttpQuotedString(input, position, true); + parameterValue = res.result; + position = res.position; - // 11.3. - const res2 = collectSequenceOfCodepoints( + // 11.8.2. + position++; + } else { // 11.9. + // 11.9.1. + const res = collectSequenceOfCodepoints( input, position, - (c) => c !== "\u003B" && c !== "\u003D", + (c) => c !== "\u003B", + ); + parameterValue = res.result; + position = res.position; + + // 11.9.2. + parameterValue = StringPrototypeReplaceAll( + parameterValue, + HTTP_WHITESPACE_SUFFIX_RE, + "", ); - let parameterName = res2.result; - position = res2.position; - - // 11.4. - parameterName = StringPrototypeToLowerCase(parameterName); - - // 11.5. - if (position < endOfInput) { - if (input[position] == "\u003B") continue; - position++; - } - - // 11.6. - if (position >= endOfInput) break; - - // 11.7. - let parameterValue = null; - - // 11.8. - if (input[position] === "\u0022") { - // 11.8.1. - const res = collectHttpQuotedString(input, position, true); - parameterValue = res.result; - position = res.position; - - // 11.8.2. - position++; - } else { // 11.9. - // 11.9.1. - const res = collectSequenceOfCodepoints( - input, - position, - (c) => c !== "\u003B", - ); - parameterValue = res.result; - position = res.position; - - // 11.9.2. - parameterValue = StringPrototypeReplaceAll( - parameterValue, - HTTP_WHITESPACE_SUFFIX_RE, - "", - ); - - // 11.9.3. - if (parameterValue === "") continue; - } - // 11.10. - if ( - parameterName !== "" && - RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, parameterName) && - RegExpPrototypeTest( - HTTP_QUOTED_STRING_TOKEN_POINT_RE, - parameterValue, - ) && - !MapPrototypeHas(mimeType.parameters, parameterName) - ) { - MapPrototypeSet(mimeType.parameters, parameterName, parameterValue); - } + // 11.9.3. + if (parameterValue === "") continue; } - // 12. - return mimeType; - } - - /** - * @param {MimeType} mimeType - * @returns {string} - */ - function essence(mimeType) { - return `${mimeType.type}/${mimeType.subtype}`; + // 11.10. + if ( + parameterName !== "" && + RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, parameterName) && + RegExpPrototypeTest( + HTTP_QUOTED_STRING_TOKEN_POINT_RE, + parameterValue, + ) && + !MapPrototypeHas(mimeType.parameters, parameterName) + ) { + MapPrototypeSet(mimeType.parameters, parameterName, parameterValue); + } } - /** - * @param {MimeType} mimeType - * @returns {string} - */ - function serializeMimeType(mimeType) { - let serialization = essence(mimeType); - for (const param of new SafeMapIterator(mimeType.parameters)) { - serialization += `;${param[0]}=`; - let value = param[1]; - if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, value)) { - value = StringPrototypeReplaceAll(value, "\\", "\\\\"); - value = StringPrototypeReplaceAll(value, '"', '\\"'); - value = `"${value}"`; - } - serialization += value; + // 12. + return mimeType; +} + +/** + * @param {MimeType} mimeType + * @returns {string} + */ +function essence(mimeType) { + return `${mimeType.type}/${mimeType.subtype}`; +} + +/** + * @param {MimeType} mimeType + * @returns {string} + */ +function serializeMimeType(mimeType) { + let serialization = essence(mimeType); + for (const param of new SafeMapIterator(mimeType.parameters)) { + serialization += `;${param[0]}=`; + let value = param[1]; + if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, value)) { + value = StringPrototypeReplaceAll(value, "\\", "\\\\"); + value = StringPrototypeReplaceAll(value, '"', '\\"'); + value = `"${value}"`; } - return serialization; + serialization += value; } - - /** - * Part of the Fetch spec's "extract a MIME type" algorithm - * (https://fetch.spec.whatwg.org/#concept-header-extract-mime-type). - * @param {string[] | null} headerValues The result of getting, decoding and - * splitting the "Content-Type" header. - * @returns {MimeType | null} - */ - function extractMimeType(headerValues) { - if (headerValues === null) return null; - - let charset = null; - let essence_ = null; - let mimeType = null; - for (let i = 0; i < headerValues.length; ++i) { - const value = headerValues[i]; - const temporaryMimeType = parseMimeType(value); + return serialization; +} + +/** + * Part of the Fetch spec's "extract a MIME type" algorithm + * (https://fetch.spec.whatwg.org/#concept-header-extract-mime-type). + * @param {string[] | null} headerValues The result of getting, decoding and + * splitting the "Content-Type" header. + * @returns {MimeType | null} + */ +function extractMimeType(headerValues) { + if (headerValues === null) return null; + + let charset = null; + let essence_ = null; + let mimeType = null; + for (let i = 0; i < headerValues.length; ++i) { + const value = headerValues[i]; + const temporaryMimeType = parseMimeType(value); + if ( + temporaryMimeType === null || + essence(temporaryMimeType) == "*/*" + ) { + continue; + } + mimeType = temporaryMimeType; + if (essence(mimeType) !== essence_) { + charset = null; + const newCharset = MapPrototypeGet(mimeType.parameters, "charset"); + if (newCharset !== undefined) { + charset = newCharset; + } + essence_ = essence(mimeType); + } else { if ( - temporaryMimeType === null || - essence(temporaryMimeType) == "*/*" + !MapPrototypeHas(mimeType.parameters, "charset") && + charset !== null ) { - continue; - } - mimeType = temporaryMimeType; - if (essence(mimeType) !== essence_) { - charset = null; - const newCharset = MapPrototypeGet(mimeType.parameters, "charset"); - if (newCharset !== undefined) { - charset = newCharset; - } - essence_ = essence(mimeType); - } else { - if ( - !MapPrototypeHas(mimeType.parameters, "charset") && - charset !== null - ) { - MapPrototypeSet(mimeType.parameters, "charset", charset); - } + MapPrototypeSet(mimeType.parameters, "charset", charset); } } - return mimeType; } + return mimeType; +} - window.__bootstrap.mimesniff = { - parseMimeType, - essence, - serializeMimeType, - extractMimeType, - }; -})(this); +export { essence, extractMimeType, parseMimeType, serializeMimeType }; diff --git a/ext/web/02_event.js b/ext/web/02_event.js index c99eb8f6e..de5210d33 100644 --- a/ext/web/02_event.js +++ b/ext/web/02_event.js @@ -4,1520 +4,1525 @@ // Many parts of the DOM are not implemented in Deno, but the logic for those // parts still exists. This means you will observe a lot of strange structures // and impossible logic branches based on what Deno currently supports. -"use strict"; - -((window) => { - const core = window.Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { DOMException } = window.__bootstrap.domException; - const consoleInternal = window.__bootstrap.console; - const { - ArrayPrototypeFilter, - ArrayPrototypeIncludes, - ArrayPrototypeIndexOf, - ArrayPrototypeMap, - ArrayPrototypePush, - ArrayPrototypeSlice, - ArrayPrototypeSplice, - ArrayPrototypeUnshift, - Boolean, - DateNow, - Error, - FunctionPrototypeCall, - Map, - MapPrototypeGet, - MapPrototypeSet, - ObjectCreate, - ObjectDefineProperty, - ObjectGetOwnPropertyDescriptor, - ObjectPrototypeIsPrototypeOf, - ReflectDefineProperty, - ReflectHas, - SafeArrayIterator, - StringPrototypeStartsWith, - Symbol, - SymbolFor, - SymbolToStringTag, - TypeError, - } = window.__bootstrap.primordials; - - // accessors for non runtime visible data - - function getDispatched(event) { - return Boolean(event[_dispatched]); - } - - function getPath(event) { - return event[_path] ?? []; - } - - function getStopImmediatePropagation(event) { - return Boolean(event[_stopImmediatePropagationFlag]); - } - - function setCurrentTarget( - event, - value, - ) { - event[_attributes].currentTarget = value; - } - - function setIsTrusted(event, value) { - event[_isTrusted] = value; - } - function setDispatched(event, value) { - event[_dispatched] = value; - } - - function setEventPhase(event, value) { - event[_attributes].eventPhase = value; +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import DOMException from "internal:ext/web/01_dom_exception.js"; +import { createFilteredInspectProxy } from "internal:ext/console/02_console.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeMap, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSplice, + ArrayPrototypeUnshift, + Boolean, + DateNow, + Error, + FunctionPrototypeCall, + Map, + MapPrototypeGet, + MapPrototypeSet, + ObjectCreate, + ObjectDefineProperty, + ObjectGetOwnPropertyDescriptor, + ObjectPrototypeIsPrototypeOf, + ReflectDefineProperty, + ReflectHas, + SafeArrayIterator, + StringPrototypeStartsWith, + Symbol, + SymbolFor, + SymbolToStringTag, + TypeError, +} = primordials; + +// This should be set via setGlobalThis this is required so that if even +// user deletes globalThis it is still usable +let globalThis_; + +function saveGlobalThisReference(val) { + globalThis_ = val; +} + +// accessors for non runtime visible data + +function getDispatched(event) { + return Boolean(event[_dispatched]); +} + +function getPath(event) { + return event[_path] ?? []; +} + +function getStopImmediatePropagation(event) { + return Boolean(event[_stopImmediatePropagationFlag]); +} + +function setCurrentTarget( + event, + value, +) { + event[_attributes].currentTarget = value; +} + +function setIsTrusted(event, value) { + event[_isTrusted] = value; +} + +function setDispatched(event, value) { + event[_dispatched] = value; +} + +function setEventPhase(event, value) { + event[_attributes].eventPhase = value; +} + +function setInPassiveListener(event, value) { + event[_inPassiveListener] = value; +} + +function setPath(event, value) { + event[_path] = value; +} + +function setRelatedTarget( + event, + value, +) { + event[_attributes].relatedTarget = value; +} + +function setTarget(event, value) { + event[_attributes].target = value; +} + +function setStopImmediatePropagation( + event, + value, +) { + event[_stopImmediatePropagationFlag] = value; +} + +// Type guards that widen the event type + +function hasRelatedTarget( + event, +) { + return ReflectHas(event, "relatedTarget"); +} + +const isTrusted = ObjectGetOwnPropertyDescriptor({ + get isTrusted() { + return this[_isTrusted]; + }, +}, "isTrusted").get; + +const eventInitConverter = webidl.createDictionaryConverter("EventInit", [{ + key: "bubbles", + defaultValue: false, + converter: webidl.converters.boolean, +}, { + key: "cancelable", + defaultValue: false, + converter: webidl.converters.boolean, +}, { + key: "composed", + defaultValue: false, + converter: webidl.converters.boolean, +}]); + +const _attributes = Symbol("[[attributes]]"); +const _canceledFlag = Symbol("[[canceledFlag]]"); +const _stopPropagationFlag = Symbol("[[stopPropagationFlag]]"); +const _stopImmediatePropagationFlag = Symbol( + "[[stopImmediatePropagationFlag]]", +); +const _inPassiveListener = Symbol("[[inPassiveListener]]"); +const _dispatched = Symbol("[[dispatched]]"); +const _isTrusted = Symbol("[[isTrusted]]"); +const _path = Symbol("[[path]]"); +// internal. +const _skipInternalInit = Symbol("[[skipSlowInit]]"); + +class Event { + constructor(type, eventInitDict = {}) { + // TODO(lucacasonato): remove when this interface is spec aligned + this[SymbolToStringTag] = "Event"; + this[_canceledFlag] = false; + this[_stopPropagationFlag] = false; + this[_stopImmediatePropagationFlag] = false; + this[_inPassiveListener] = false; + this[_dispatched] = false; + this[_isTrusted] = false; + this[_path] = []; + + if (!eventInitDict[_skipInternalInit]) { + webidl.requiredArguments(arguments.length, 1, { + prefix: "Failed to construct 'Event'", + }); + type = webidl.converters.DOMString(type, { + prefix: "Failed to construct 'Event'", + context: "Argument 1", + }); + const eventInit = eventInitConverter(eventInitDict, { + prefix: "Failed to construct 'Event'", + context: "Argument 2", + }); + this[_attributes] = { + type, + ...eventInit, + currentTarget: null, + eventPhase: Event.NONE, + target: null, + timeStamp: DateNow(), + }; + // [LegacyUnforgeable] + ReflectDefineProperty(this, "isTrusted", { + enumerable: true, + get: isTrusted, + }); + } else { + this[_attributes] = { + type, + data: eventInitDict.data ?? null, + bubbles: eventInitDict.bubbles ?? false, + cancelable: eventInitDict.cancelable ?? false, + composed: eventInitDict.composed ?? false, + currentTarget: null, + eventPhase: Event.NONE, + target: null, + timeStamp: DateNow(), + }; + // TODO(@littledivy): Not spec compliant but performance is hurt badly + // for users of `_skipInternalInit`. + this.isTrusted = false; + } } - function setInPassiveListener(event, value) { - event[_inPassiveListener] = value; + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(Event.prototype, this), + keys: EVENT_PROPS, + })); } - function setPath(event, value) { - event[_path] = value; + get type() { + return this[_attributes].type; } - function setRelatedTarget( - event, - value, - ) { - event[_attributes].relatedTarget = value; + get target() { + return this[_attributes].target; } - function setTarget(event, value) { - event[_attributes].target = value; + get srcElement() { + return null; } - function setStopImmediatePropagation( - event, - value, - ) { - event[_stopImmediatePropagationFlag] = value; + set srcElement(_) { + // this member is deprecated } - // Type guards that widen the event type - - function hasRelatedTarget( - event, - ) { - return ReflectHas(event, "relatedTarget"); + get currentTarget() { + return this[_attributes].currentTarget; } - const isTrusted = ObjectGetOwnPropertyDescriptor({ - get isTrusted() { - return this[_isTrusted]; - }, - }, "isTrusted").get; - - const eventInitConverter = webidl.createDictionaryConverter("EventInit", [{ - key: "bubbles", - defaultValue: false, - converter: webidl.converters.boolean, - }, { - key: "cancelable", - defaultValue: false, - converter: webidl.converters.boolean, - }, { - key: "composed", - defaultValue: false, - converter: webidl.converters.boolean, - }]); - - const _attributes = Symbol("[[attributes]]"); - const _canceledFlag = Symbol("[[canceledFlag]]"); - const _stopPropagationFlag = Symbol("[[stopPropagationFlag]]"); - const _stopImmediatePropagationFlag = Symbol( - "[[stopImmediatePropagationFlag]]", - ); - const _inPassiveListener = Symbol("[[inPassiveListener]]"); - const _dispatched = Symbol("[[dispatched]]"); - const _isTrusted = Symbol("[[isTrusted]]"); - const _path = Symbol("[[path]]"); - // internal. - const _skipInternalInit = Symbol("[[skipSlowInit]]"); - - class Event { - constructor(type, eventInitDict = {}) { - // TODO(lucacasonato): remove when this interface is spec aligned - this[SymbolToStringTag] = "Event"; - this[_canceledFlag] = false; - this[_stopPropagationFlag] = false; - this[_stopImmediatePropagationFlag] = false; - this[_inPassiveListener] = false; - this[_dispatched] = false; - this[_isTrusted] = false; - this[_path] = []; - - if (!eventInitDict[_skipInternalInit]) { - webidl.requiredArguments(arguments.length, 1, { - prefix: "Failed to construct 'Event'", - }); - type = webidl.converters.DOMString(type, { - prefix: "Failed to construct 'Event'", - context: "Argument 1", - }); - const eventInit = eventInitConverter(eventInitDict, { - prefix: "Failed to construct 'Event'", - context: "Argument 2", - }); - this[_attributes] = { - type, - ...eventInit, - currentTarget: null, - eventPhase: Event.NONE, - target: null, - timeStamp: DateNow(), - }; - // [LegacyUnforgeable] - ReflectDefineProperty(this, "isTrusted", { - enumerable: true, - get: isTrusted, - }); - } else { - this[_attributes] = { - type, - data: eventInitDict.data ?? null, - bubbles: eventInitDict.bubbles ?? false, - cancelable: eventInitDict.cancelable ?? false, - composed: eventInitDict.composed ?? false, - currentTarget: null, - eventPhase: Event.NONE, - target: null, - timeStamp: DateNow(), - }; - // TODO(@littledivy): Not spec compliant but performance is hurt badly - // for users of `_skipInternalInit`. - this.isTrusted = false; - } + composedPath() { + const path = this[_path]; + if (path.length === 0) { + return []; } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(Event.prototype, this), - keys: EVENT_PROPS, - })); + if (!this.currentTarget) { + throw new Error("assertion error"); } + const composedPath = [ + { + item: this.currentTarget, + itemInShadowTree: false, + relatedTarget: null, + rootOfClosedTree: false, + slotInClosedTree: false, + target: null, + touchTargetList: [], + }, + ]; - get type() { - return this[_attributes].type; - } + let currentTargetIndex = 0; + let currentTargetHiddenSubtreeLevel = 0; - get target() { - return this[_attributes].target; - } + for (let index = path.length - 1; index >= 0; index--) { + const { item, rootOfClosedTree, slotInClosedTree } = path[index]; - get srcElement() { - return null; - } + if (rootOfClosedTree) { + currentTargetHiddenSubtreeLevel++; + } - set srcElement(_) { - // this member is deprecated - } + if (item === this.currentTarget) { + currentTargetIndex = index; + break; + } - get currentTarget() { - return this[_attributes].currentTarget; + if (slotInClosedTree) { + currentTargetHiddenSubtreeLevel--; + } } - composedPath() { - const path = this[_path]; - if (path.length === 0) { - return []; - } + let currentHiddenLevel = currentTargetHiddenSubtreeLevel; + let maxHiddenLevel = currentTargetHiddenSubtreeLevel; + + for (let i = currentTargetIndex - 1; i >= 0; i--) { + const { item, rootOfClosedTree, slotInClosedTree } = path[i]; - if (!this.currentTarget) { - throw new Error("assertion error"); + if (rootOfClosedTree) { + currentHiddenLevel++; } - const composedPath = [ - { - item: this.currentTarget, + + if (currentHiddenLevel <= maxHiddenLevel) { + ArrayPrototypeUnshift(composedPath, { + item, itemInShadowTree: false, relatedTarget: null, rootOfClosedTree: false, slotInClosedTree: false, target: null, touchTargetList: [], - }, - ]; - - let currentTargetIndex = 0; - let currentTargetHiddenSubtreeLevel = 0; - - for (let index = path.length - 1; index >= 0; index--) { - const { item, rootOfClosedTree, slotInClosedTree } = path[index]; - - if (rootOfClosedTree) { - currentTargetHiddenSubtreeLevel++; - } - - if (item === this.currentTarget) { - currentTargetIndex = index; - break; - } - - if (slotInClosedTree) { - currentTargetHiddenSubtreeLevel--; - } + }); } - let currentHiddenLevel = currentTargetHiddenSubtreeLevel; - let maxHiddenLevel = currentTargetHiddenSubtreeLevel; - - for (let i = currentTargetIndex - 1; i >= 0; i--) { - const { item, rootOfClosedTree, slotInClosedTree } = path[i]; - - if (rootOfClosedTree) { - currentHiddenLevel++; - } - - if (currentHiddenLevel <= maxHiddenLevel) { - ArrayPrototypeUnshift(composedPath, { - item, - itemInShadowTree: false, - relatedTarget: null, - rootOfClosedTree: false, - slotInClosedTree: false, - target: null, - touchTargetList: [], - }); - } - - if (slotInClosedTree) { - currentHiddenLevel--; + if (slotInClosedTree) { + currentHiddenLevel--; - if (currentHiddenLevel < maxHiddenLevel) { - maxHiddenLevel = currentHiddenLevel; - } + if (currentHiddenLevel < maxHiddenLevel) { + maxHiddenLevel = currentHiddenLevel; } } + } - currentHiddenLevel = currentTargetHiddenSubtreeLevel; - maxHiddenLevel = currentTargetHiddenSubtreeLevel; + currentHiddenLevel = currentTargetHiddenSubtreeLevel; + maxHiddenLevel = currentTargetHiddenSubtreeLevel; - for (let index = currentTargetIndex + 1; index < path.length; index++) { - const { item, rootOfClosedTree, slotInClosedTree } = path[index]; + for (let index = currentTargetIndex + 1; index < path.length; index++) { + const { item, rootOfClosedTree, slotInClosedTree } = path[index]; - if (slotInClosedTree) { - currentHiddenLevel++; - } + if (slotInClosedTree) { + currentHiddenLevel++; + } - if (currentHiddenLevel <= maxHiddenLevel) { - ArrayPrototypePush(composedPath, { - item, - itemInShadowTree: false, - relatedTarget: null, - rootOfClosedTree: false, - slotInClosedTree: false, - target: null, - touchTargetList: [], - }); - } + if (currentHiddenLevel <= maxHiddenLevel) { + ArrayPrototypePush(composedPath, { + item, + itemInShadowTree: false, + relatedTarget: null, + rootOfClosedTree: false, + slotInClosedTree: false, + target: null, + touchTargetList: [], + }); + } - if (rootOfClosedTree) { - currentHiddenLevel--; + if (rootOfClosedTree) { + currentHiddenLevel--; - if (currentHiddenLevel < maxHiddenLevel) { - maxHiddenLevel = currentHiddenLevel; - } + if (currentHiddenLevel < maxHiddenLevel) { + maxHiddenLevel = currentHiddenLevel; } } - return ArrayPrototypeMap(composedPath, (p) => p.item); - } - - get NONE() { - return Event.NONE; - } - - get CAPTURING_PHASE() { - return Event.CAPTURING_PHASE; - } - - get AT_TARGET() { - return Event.AT_TARGET; - } - - get BUBBLING_PHASE() { - return Event.BUBBLING_PHASE; - } - - static get NONE() { - return 0; } + return ArrayPrototypeMap(composedPath, (p) => p.item); + } - static get CAPTURING_PHASE() { - return 1; - } + get NONE() { + return Event.NONE; + } - static get AT_TARGET() { - return 2; - } + get CAPTURING_PHASE() { + return Event.CAPTURING_PHASE; + } - static get BUBBLING_PHASE() { - return 3; - } + get AT_TARGET() { + return Event.AT_TARGET; + } - get eventPhase() { - return this[_attributes].eventPhase; - } + get BUBBLING_PHASE() { + return Event.BUBBLING_PHASE; + } - stopPropagation() { - this[_stopPropagationFlag] = true; - } + static get NONE() { + return 0; + } - get cancelBubble() { - return this[_stopPropagationFlag]; - } + static get CAPTURING_PHASE() { + return 1; + } - set cancelBubble(value) { - this[_stopPropagationFlag] = webidl.converters.boolean(value); - } + static get AT_TARGET() { + return 2; + } - stopImmediatePropagation() { - this[_stopPropagationFlag] = true; - this[_stopImmediatePropagationFlag] = true; - } + static get BUBBLING_PHASE() { + return 3; + } - get bubbles() { - return this[_attributes].bubbles; - } + get eventPhase() { + return this[_attributes].eventPhase; + } - get cancelable() { - return this[_attributes].cancelable; - } + stopPropagation() { + this[_stopPropagationFlag] = true; + } - get returnValue() { - return !this[_canceledFlag]; - } + get cancelBubble() { + return this[_stopPropagationFlag]; + } - set returnValue(value) { - if (!webidl.converters.boolean(value)) { - this[_canceledFlag] = true; - } - } + set cancelBubble(value) { + this[_stopPropagationFlag] = webidl.converters.boolean(value); + } - preventDefault() { - if (this[_attributes].cancelable && !this[_inPassiveListener]) { - this[_canceledFlag] = true; - } - } + stopImmediatePropagation() { + this[_stopPropagationFlag] = true; + this[_stopImmediatePropagationFlag] = true; + } - get defaultPrevented() { - return this[_canceledFlag]; - } + get bubbles() { + return this[_attributes].bubbles; + } - get composed() { - return this[_attributes].composed; - } + get cancelable() { + return this[_attributes].cancelable; + } - get initialized() { - return true; - } + get returnValue() { + return !this[_canceledFlag]; + } - get timeStamp() { - return this[_attributes].timeStamp; + set returnValue(value) { + if (!webidl.converters.boolean(value)) { + this[_canceledFlag] = true; } } - function defineEnumerableProps( - Ctor, - props, - ) { - for (let i = 0; i < props.length; ++i) { - const prop = props[i]; - ReflectDefineProperty(Ctor.prototype, prop, { enumerable: true }); + preventDefault() { + if (this[_attributes].cancelable && !this[_inPassiveListener]) { + this[_canceledFlag] = true; } } - const EVENT_PROPS = [ - "bubbles", - "cancelable", - "composed", - "currentTarget", - "defaultPrevented", - "eventPhase", - "srcElement", - "target", - "returnValue", - "timeStamp", - "type", - ]; - - defineEnumerableProps(Event, EVENT_PROPS); - - // This is currently the only node type we are using, so instead of implementing - // the whole of the Node interface at the moment, this just gives us the one - // value to power the standards based logic - const DOCUMENT_FRAGMENT_NODE = 11; - - // DOM Logic Helper functions and type guards - - /** Get the parent node, for event targets that have a parent. - * - * Ref: https://dom.spec.whatwg.org/#get-the-parent */ - function getParent(eventTarget) { - return isNode(eventTarget) ? eventTarget.parentNode : null; + get defaultPrevented() { + return this[_canceledFlag]; } - function getRoot(eventTarget) { - return isNode(eventTarget) - ? eventTarget.getRootNode({ composed: true }) - : null; + get composed() { + return this[_attributes].composed; } - function isNode( - eventTarget, - ) { - return Boolean(eventTarget && ReflectHas(eventTarget, "nodeType")); + get initialized() { + return true; } - // https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor - function isShadowInclusiveAncestor( - ancestor, - node, - ) { - while (isNode(node)) { - if (node === ancestor) { - return true; - } - - if (isShadowRoot(node)) { - node = node && getHost(node); - } else { - node = getParent(node); - } - } - - return false; + get timeStamp() { + return this[_attributes].timeStamp; } - - function isShadowRoot(nodeImpl) { - return Boolean( - nodeImpl && - isNode(nodeImpl) && - nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE && - getHost(nodeImpl) != null, - ); +} + +function defineEnumerableProps( + Ctor, + props, +) { + for (let i = 0; i < props.length; ++i) { + const prop = props[i]; + ReflectDefineProperty(Ctor.prototype, prop, { enumerable: true }); } +} + +const EVENT_PROPS = [ + "bubbles", + "cancelable", + "composed", + "currentTarget", + "defaultPrevented", + "eventPhase", + "srcElement", + "target", + "returnValue", + "timeStamp", + "type", +]; + +defineEnumerableProps(Event, EVENT_PROPS); + +// This is currently the only node type we are using, so instead of implementing +// the whole of the Node interface at the moment, this just gives us the one +// value to power the standards based logic +const DOCUMENT_FRAGMENT_NODE = 11; + +// DOM Logic Helper functions and type guards + +/** Get the parent node, for event targets that have a parent. + * + * Ref: https://dom.spec.whatwg.org/#get-the-parent */ +function getParent(eventTarget) { + return isNode(eventTarget) ? eventTarget.parentNode : null; +} + +function getRoot(eventTarget) { + return isNode(eventTarget) + ? eventTarget.getRootNode({ composed: true }) + : null; +} + +function isNode( + eventTarget, +) { + return Boolean(eventTarget && ReflectHas(eventTarget, "nodeType")); +} + +// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor +function isShadowInclusiveAncestor( + ancestor, + node, +) { + while (isNode(node)) { + if (node === ancestor) { + return true; + } - function isSlotable( - nodeImpl, - ) { - return Boolean(isNode(nodeImpl) && ReflectHas(nodeImpl, "assignedSlot")); + if (isShadowRoot(node)) { + node = node && getHost(node); + } else { + node = getParent(node); + } } - // DOM Logic functions + return false; +} - /** Append a path item to an event's path. - * - * Ref: https://dom.spec.whatwg.org/#concept-event-path-append - */ - function appendToEventPath( - eventImpl, - target, - targetOverride, +function isShadowRoot(nodeImpl) { + return Boolean( + nodeImpl && + isNode(nodeImpl) && + nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE && + getHost(nodeImpl) != null, + ); +} + +function isSlotable( + nodeImpl, +) { + return Boolean(isNode(nodeImpl) && ReflectHas(nodeImpl, "assignedSlot")); +} + +// DOM Logic functions + +/** Append a path item to an event's path. + * + * Ref: https://dom.spec.whatwg.org/#concept-event-path-append + */ +function appendToEventPath( + eventImpl, + target, + targetOverride, + relatedTarget, + touchTargets, + slotInClosedTree, +) { + const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target)); + const rootOfClosedTree = isShadowRoot(target) && + getMode(target) === "closed"; + + ArrayPrototypePush(getPath(eventImpl), { + item: target, + itemInShadowTree, + target: targetOverride, relatedTarget, - touchTargets, + touchTargetList: touchTargets, + rootOfClosedTree, slotInClosedTree, - ) { - const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target)); - const rootOfClosedTree = isShadowRoot(target) && - getMode(target) === "closed"; - - ArrayPrototypePush(getPath(eventImpl), { - item: target, - itemInShadowTree, - target: targetOverride, + }); +} + +function dispatch( + targetImpl, + eventImpl, + targetOverride, +) { + let clearTargets = false; + let activationTarget = null; + + setDispatched(eventImpl, true); + + targetOverride = targetOverride ?? targetImpl; + const eventRelatedTarget = hasRelatedTarget(eventImpl) + ? eventImpl.relatedTarget + : null; + let relatedTarget = retarget(eventRelatedTarget, targetImpl); + + if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) { + const touchTargets = []; + + appendToEventPath( + eventImpl, + targetImpl, + targetOverride, relatedTarget, - touchTargetList: touchTargets, - rootOfClosedTree, - slotInClosedTree, - }); - } + touchTargets, + false, + ); - function dispatch( - targetImpl, - eventImpl, - targetOverride, - ) { - let clearTargets = false; - let activationTarget = null; + const isActivationEvent = eventImpl.type === "click"; - setDispatched(eventImpl, true); + if (isActivationEvent && getHasActivationBehavior(targetImpl)) { + activationTarget = targetImpl; + } - targetOverride = targetOverride ?? targetImpl; - const eventRelatedTarget = hasRelatedTarget(eventImpl) - ? eventImpl.relatedTarget + let slotInClosedTree = false; + let slotable = isSlotable(targetImpl) && getAssignedSlot(targetImpl) + ? targetImpl : null; - let relatedTarget = retarget(eventRelatedTarget, targetImpl); - - if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) { - const touchTargets = []; - - appendToEventPath( - eventImpl, - targetImpl, - targetOverride, - relatedTarget, - touchTargets, - false, - ); - - const isActivationEvent = eventImpl.type === "click"; + let parent = getParent(targetImpl); - if (isActivationEvent && getHasActivationBehavior(targetImpl)) { - activationTarget = targetImpl; - } - - let slotInClosedTree = false; - let slotable = isSlotable(targetImpl) && getAssignedSlot(targetImpl) - ? targetImpl - : null; - let parent = getParent(targetImpl); - - // Populate event path - // https://dom.spec.whatwg.org/#event-path - while (parent !== null) { - if (slotable !== null) { - slotable = null; - - const parentRoot = getRoot(parent); - if ( - isShadowRoot(parentRoot) && - parentRoot && - getMode(parentRoot) === "closed" - ) { - slotInClosedTree = true; - } - } - - relatedTarget = retarget(eventRelatedTarget, parent); + // Populate event path + // https://dom.spec.whatwg.org/#event-path + while (parent !== null) { + if (slotable !== null) { + slotable = null; + const parentRoot = getRoot(parent); if ( - isNode(parent) && - isShadowInclusiveAncestor(getRoot(targetImpl), parent) + isShadowRoot(parentRoot) && + parentRoot && + getMode(parentRoot) === "closed" ) { - appendToEventPath( - eventImpl, - parent, - null, - relatedTarget, - touchTargets, - slotInClosedTree, - ); - } else if (parent === relatedTarget) { - parent = null; - } else { - targetImpl = parent; - - if ( - isActivationEvent && - activationTarget === null && - getHasActivationBehavior(targetImpl) - ) { - activationTarget = targetImpl; - } - - appendToEventPath( - eventImpl, - parent, - targetImpl, - relatedTarget, - touchTargets, - slotInClosedTree, - ); - } - - if (parent !== null) { - parent = getParent(parent); - } - - slotInClosedTree = false; - } - - let clearTargetsTupleIndex = -1; - const path = getPath(eventImpl); - for ( - let i = path.length - 1; - i >= 0 && clearTargetsTupleIndex === -1; - i-- - ) { - if (path[i].target !== null) { - clearTargetsTupleIndex = i; - } - } - const clearTargetsTuple = path[clearTargetsTupleIndex]; - - clearTargets = (isNode(clearTargetsTuple.target) && - isShadowRoot(getRoot(clearTargetsTuple.target))) || - (isNode(clearTargetsTuple.relatedTarget) && - isShadowRoot(getRoot(clearTargetsTuple.relatedTarget))); - - setEventPhase(eventImpl, Event.CAPTURING_PHASE); - - for (let i = path.length - 1; i >= 0; --i) { - const tuple = path[i]; - - if (tuple.target === null) { - invokeEventListeners(tuple, eventImpl); + slotInClosedTree = true; } } - for (let i = 0; i < path.length; i++) { - const tuple = path[i]; + relatedTarget = retarget(eventRelatedTarget, parent); - if (tuple.target !== null) { - setEventPhase(eventImpl, Event.AT_TARGET); - } else { - setEventPhase(eventImpl, Event.BUBBLING_PHASE); - } + if ( + isNode(parent) && + isShadowInclusiveAncestor(getRoot(targetImpl), parent) + ) { + appendToEventPath( + eventImpl, + parent, + null, + relatedTarget, + touchTargets, + slotInClosedTree, + ); + } else if (parent === relatedTarget) { + parent = null; + } else { + targetImpl = parent; if ( - (eventImpl.eventPhase === Event.BUBBLING_PHASE && - eventImpl.bubbles) || - eventImpl.eventPhase === Event.AT_TARGET + isActivationEvent && + activationTarget === null && + getHasActivationBehavior(targetImpl) ) { - invokeEventListeners(tuple, eventImpl); + activationTarget = targetImpl; } + + appendToEventPath( + eventImpl, + parent, + targetImpl, + relatedTarget, + touchTargets, + slotInClosedTree, + ); } - } - setEventPhase(eventImpl, Event.NONE); - setCurrentTarget(eventImpl, null); - setPath(eventImpl, []); - setDispatched(eventImpl, false); - eventImpl.cancelBubble = false; - setStopImmediatePropagation(eventImpl, false); + if (parent !== null) { + parent = getParent(parent); + } - if (clearTargets) { - setTarget(eventImpl, null); - setRelatedTarget(eventImpl, null); + slotInClosedTree = false; } - // TODO(bartlomieju): invoke activation targets if HTML nodes will be implemented - // if (activationTarget !== null) { - // if (!eventImpl.defaultPrevented) { - // activationTarget._activationBehavior(); - // } - // } + let clearTargetsTupleIndex = -1; + const path = getPath(eventImpl); + for ( + let i = path.length - 1; + i >= 0 && clearTargetsTupleIndex === -1; + i-- + ) { + if (path[i].target !== null) { + clearTargetsTupleIndex = i; + } + } + const clearTargetsTuple = path[clearTargetsTupleIndex]; - return !eventImpl.defaultPrevented; - } + clearTargets = (isNode(clearTargetsTuple.target) && + isShadowRoot(getRoot(clearTargetsTuple.target))) || + (isNode(clearTargetsTuple.relatedTarget) && + isShadowRoot(getRoot(clearTargetsTuple.relatedTarget))); - /** Inner invoking of the event listeners where the resolved listeners are - * called. - * - * Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */ - function innerInvokeEventListeners( - eventImpl, - targetListeners, - ) { - let found = false; + setEventPhase(eventImpl, Event.CAPTURING_PHASE); - const { type } = eventImpl; + for (let i = path.length - 1; i >= 0; --i) { + const tuple = path[i]; - if (!targetListeners || !targetListeners[type]) { - return found; + if (tuple.target === null) { + invokeEventListeners(tuple, eventImpl); + } } - // Copy event listeners before iterating since the list can be modified during the iteration. - const handlers = ArrayPrototypeSlice(targetListeners[type]); + for (let i = 0; i < path.length; i++) { + const tuple = path[i]; - for (let i = 0; i < handlers.length; i++) { - const listener = handlers[i]; - - let capture, once, passive; - if (typeof listener.options === "boolean") { - capture = listener.options; - once = false; - passive = false; + if (tuple.target !== null) { + setEventPhase(eventImpl, Event.AT_TARGET); } else { - capture = listener.options.capture; - once = listener.options.once; - passive = listener.options.passive; - } - - // Check if the event listener has been removed since the listeners has been cloned. - if (!ArrayPrototypeIncludes(targetListeners[type], listener)) { - continue; + setEventPhase(eventImpl, Event.BUBBLING_PHASE); } - found = true; - if ( - (eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) || - (eventImpl.eventPhase === Event.BUBBLING_PHASE && capture) + (eventImpl.eventPhase === Event.BUBBLING_PHASE && + eventImpl.bubbles) || + eventImpl.eventPhase === Event.AT_TARGET ) { - continue; - } - - if (once) { - ArrayPrototypeSplice( - targetListeners[type], - ArrayPrototypeIndexOf(targetListeners[type], listener), - 1, - ); - } - - if (passive) { - setInPassiveListener(eventImpl, true); - } - - if (typeof listener.callback === "object") { - if (typeof listener.callback.handleEvent === "function") { - listener.callback.handleEvent(eventImpl); - } - } else { - FunctionPrototypeCall( - listener.callback, - eventImpl.currentTarget, - eventImpl, - ); + invokeEventListeners(tuple, eventImpl); } + } + } - setInPassiveListener(eventImpl, false); + setEventPhase(eventImpl, Event.NONE); + setCurrentTarget(eventImpl, null); + setPath(eventImpl, []); + setDispatched(eventImpl, false); + eventImpl.cancelBubble = false; + setStopImmediatePropagation(eventImpl, false); - if (getStopImmediatePropagation(eventImpl)) { - return found; - } - } + if (clearTargets) { + setTarget(eventImpl, null); + setRelatedTarget(eventImpl, null); + } + // TODO(bartlomieju): invoke activation targets if HTML nodes will be implemented + // if (activationTarget !== null) { + // if (!eventImpl.defaultPrevented) { + // activationTarget._activationBehavior(); + // } + // } + + return !eventImpl.defaultPrevented; +} + +/** Inner invoking of the event listeners where the resolved listeners are + * called. + * + * Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */ +function innerInvokeEventListeners( + eventImpl, + targetListeners, +) { + let found = false; + + const { type } = eventImpl; + + if (!targetListeners || !targetListeners[type]) { return found; } - /** Invokes the listeners on a given event path with the supplied event. - * - * Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */ - function invokeEventListeners(tuple, eventImpl) { - const path = getPath(eventImpl); - const tupleIndex = ArrayPrototypeIndexOf(path, tuple); - for (let i = tupleIndex; i >= 0; i--) { - const t = path[i]; - if (t.target) { - setTarget(eventImpl, t.target); - break; - } - } + // Copy event listeners before iterating since the list can be modified during the iteration. + const handlers = ArrayPrototypeSlice(targetListeners[type]); - setRelatedTarget(eventImpl, tuple.relatedTarget); + for (let i = 0; i < handlers.length; i++) { + const listener = handlers[i]; - if (eventImpl.cancelBubble) { - return; + let capture, once, passive; + if (typeof listener.options === "boolean") { + capture = listener.options; + once = false; + passive = false; + } else { + capture = listener.options.capture; + once = listener.options.once; + passive = listener.options.passive; } - setCurrentTarget(eventImpl, tuple.item); - - try { - innerInvokeEventListeners(eventImpl, getListeners(tuple.item)); - } catch (error) { - reportException(error); + // Check if the event listener has been removed since the listeners has been cloned. + if (!ArrayPrototypeIncludes(targetListeners[type], listener)) { + continue; } - } - function normalizeEventHandlerOptions( - options, - ) { - if (typeof options === "boolean" || typeof options === "undefined") { - return { - capture: Boolean(options), - }; - } else { - return options; - } - } + found = true; - /** Retarget the target following the spec logic. - * - * Ref: https://dom.spec.whatwg.org/#retarget */ - function retarget(a, b) { - while (true) { - if (!isNode(a)) { - return a; - } + if ( + (eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) || + (eventImpl.eventPhase === Event.BUBBLING_PHASE && capture) + ) { + continue; + } - const aRoot = a.getRootNode(); + if (once) { + ArrayPrototypeSplice( + targetListeners[type], + ArrayPrototypeIndexOf(targetListeners[type], listener), + 1, + ); + } - if (aRoot) { - if ( - !isShadowRoot(aRoot) || - (isNode(b) && isShadowInclusiveAncestor(aRoot, b)) - ) { - return a; - } + if (passive) { + setInPassiveListener(eventImpl, true); + } - a = getHost(aRoot); + if (typeof listener.callback === "object") { + if (typeof listener.callback.handleEvent === "function") { + listener.callback.handleEvent(eventImpl); } + } else { + FunctionPrototypeCall( + listener.callback, + eventImpl.currentTarget, + eventImpl, + ); } - } - // Accessors for non-public data + setInPassiveListener(eventImpl, false); - const eventTargetData = Symbol(); - - function setEventTargetData(target) { - target[eventTargetData] = getDefaultTargetData(); - } - - function getAssignedSlot(target) { - return Boolean(target?.[eventTargetData]?.assignedSlot); + if (getStopImmediatePropagation(eventImpl)) { + return found; + } } - function getHasActivationBehavior(target) { - return Boolean(target?.[eventTargetData]?.hasActivationBehavior); + return found; +} + +/** Invokes the listeners on a given event path with the supplied event. + * + * Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */ +function invokeEventListeners(tuple, eventImpl) { + const path = getPath(eventImpl); + const tupleIndex = ArrayPrototypeIndexOf(path, tuple); + for (let i = tupleIndex; i >= 0; i--) { + const t = path[i]; + if (t.target) { + setTarget(eventImpl, t.target); + break; + } } - function getHost(target) { - return target?.[eventTargetData]?.host ?? null; - } + setRelatedTarget(eventImpl, tuple.relatedTarget); - function getListeners(target) { - return target?.[eventTargetData]?.listeners ?? {}; + if (eventImpl.cancelBubble) { + return; } - function getMode(target) { - return target?.[eventTargetData]?.mode ?? null; - } + setCurrentTarget(eventImpl, tuple.item); - function listenerCount(target, type) { - return getListeners(target)?.[type]?.length ?? 0; + try { + innerInvokeEventListeners(eventImpl, getListeners(tuple.item)); + } catch (error) { + reportException(error); } +} - function getDefaultTargetData() { +function normalizeEventHandlerOptions( + options, +) { + if (typeof options === "boolean" || typeof options === "undefined") { return { - assignedSlot: false, - hasActivationBehavior: false, - host: null, - listeners: ObjectCreate(null), - mode: "", + capture: Boolean(options), }; + } else { + return options; } +} - // This is lazy loaded because there is a circular dependency with AbortSignal. - let addEventListenerOptionsConverter; - - function lazyAddEventListenerOptionsConverter() { - addEventListenerOptionsConverter ??= webidl.createDictionaryConverter( - "AddEventListenerOptions", - [ - { - key: "capture", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "passive", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "once", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "signal", - converter: webidl.converters.AbortSignal, - }, - ], - ); - } - - webidl.converters.AddEventListenerOptions = (V, opts) => { - if (webidl.type(V) !== "Object" || V === null) { - V = { capture: Boolean(V) }; - } - - lazyAddEventListenerOptionsConverter(); - return addEventListenerOptionsConverter(V, opts); - }; - - class EventTarget { - constructor() { - this[eventTargetData] = getDefaultTargetData(); - this[webidl.brand] = webidl.brand; +/** Retarget the target following the spec logic. + * + * Ref: https://dom.spec.whatwg.org/#retarget */ +function retarget(a, b) { + while (true) { + if (!isNode(a)) { + return a; } - addEventListener( - type, - callback, - options, - ) { - const self = this ?? globalThis; - webidl.assertBranded(self, EventTargetPrototype); - const prefix = "Failed to execute 'addEventListener' on 'EventTarget'"; + const aRoot = a.getRootNode(); - webidl.requiredArguments(arguments.length, 2, { - prefix, - }); + if (aRoot) { + if ( + !isShadowRoot(aRoot) || + (isNode(b) && isShadowInclusiveAncestor(aRoot, b)) + ) { + return a; + } - options = webidl.converters.AddEventListenerOptions(options, { - prefix, - context: "Argument 3", - }); + a = getHost(aRoot); + } + } +} - if (callback === null) { - return; - } +// Accessors for non-public data - const { listeners } = self[eventTargetData]; +const eventTargetData = Symbol(); - if (!(ReflectHas(listeners, type))) { - listeners[type] = []; - } +function setEventTargetData(target) { + target[eventTargetData] = getDefaultTargetData(); +} - const listenerList = listeners[type]; - for (let i = 0; i < listenerList.length; ++i) { - const listener = listenerList[i]; - if ( - ((typeof listener.options === "boolean" && - listener.options === options.capture) || - (typeof listener.options === "object" && - listener.options.capture === options.capture)) && - listener.callback === callback - ) { - return; - } - } - if (options?.signal) { - const signal = options?.signal; - if (signal.aborted) { - // If signal is not null and its aborted flag is set, then return. - return; - } else { - // If listener’s signal is not null, then add the following abort - // abort steps to it: Remove an event listener. - signal.addEventListener("abort", () => { - self.removeEventListener(type, callback, options); - }); - } - } +function getAssignedSlot(target) { + return Boolean(target?.[eventTargetData]?.assignedSlot); +} - ArrayPrototypePush(listeners[type], { callback, options }); - } +function getHasActivationBehavior(target) { + return Boolean(target?.[eventTargetData]?.hasActivationBehavior); +} - removeEventListener( - type, - callback, - options, - ) { - const self = this ?? globalThis; - webidl.assertBranded(self, EventTargetPrototype); - webidl.requiredArguments(arguments.length, 2, { - prefix: "Failed to execute 'removeEventListener' on 'EventTarget'", - }); +function getHost(target) { + return target?.[eventTargetData]?.host ?? null; +} - const { listeners } = self[eventTargetData]; - if (callback !== null && ReflectHas(listeners, type)) { - listeners[type] = ArrayPrototypeFilter( - listeners[type], - (listener) => listener.callback !== callback, - ); - } else if (callback === null || !listeners[type]) { - return; - } +function getListeners(target) { + return target?.[eventTargetData]?.listeners ?? {}; +} - options = normalizeEventHandlerOptions(options); +function getMode(target) { + return target?.[eventTargetData]?.mode ?? null; +} - for (let i = 0; i < listeners[type].length; ++i) { - const listener = listeners[type][i]; - if ( - ((typeof listener.options === "boolean" && - listener.options === options.capture) || - (typeof listener.options === "object" && - listener.options.capture === options.capture)) && - listener.callback === callback - ) { - ArrayPrototypeSplice(listeners[type], i, 1); - break; - } - } - } +function listenerCount(target, type) { + return getListeners(target)?.[type]?.length ?? 0; +} - dispatchEvent(event) { - // If `this` is not present, then fallback to global scope. We don't use - // `globalThis` directly here, because it could be deleted by user. - // Instead use saved reference to global scope when the script was - // executed. - const self = this ?? window; - webidl.assertBranded(self, EventTargetPrototype); - webidl.requiredArguments(arguments.length, 1, { - prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'", - }); +function getDefaultTargetData() { + return { + assignedSlot: false, + hasActivationBehavior: false, + host: null, + listeners: ObjectCreate(null), + mode: "", + }; +} - const { listeners } = self[eventTargetData]; - if (!ReflectHas(listeners, event.type)) { - setTarget(event, this); - return true; - } +// This is lazy loaded because there is a circular dependency with AbortSignal. +let addEventListenerOptionsConverter; - if (getDispatched(event)) { - throw new DOMException("Invalid event state.", "InvalidStateError"); - } +function lazyAddEventListenerOptionsConverter() { + addEventListenerOptionsConverter ??= webidl.createDictionaryConverter( + "AddEventListenerOptions", + [ + { + key: "capture", + defaultValue: false, + converter: webidl.converters.boolean, + }, + { + key: "passive", + defaultValue: false, + converter: webidl.converters.boolean, + }, + { + key: "once", + defaultValue: false, + converter: webidl.converters.boolean, + }, + { + key: "signal", + converter: webidl.converters.AbortSignal, + }, + ], + ); +} - if (event.eventPhase !== Event.NONE) { - throw new DOMException("Invalid event state.", "InvalidStateError"); - } +webidl.converters.AddEventListenerOptions = (V, opts) => { + if (webidl.type(V) !== "Object" || V === null) { + V = { capture: Boolean(V) }; + } - return dispatch(self, event); - } + lazyAddEventListenerOptionsConverter(); + return addEventListenerOptionsConverter(V, opts); +}; - getParent(_event) { - return null; - } +class EventTarget { + constructor() { + this[eventTargetData] = getDefaultTargetData(); + this[webidl.brand] = webidl.brand; } - webidl.configurePrototype(EventTarget); - const EventTargetPrototype = EventTarget.prototype; + addEventListener( + type, + callback, + options, + ) { + const self = this ?? globalThis_; + webidl.assertBranded(self, EventTargetPrototype); + const prefix = "Failed to execute 'addEventListener' on 'EventTarget'"; - defineEnumerableProps(EventTarget, [ - "addEventListener", - "removeEventListener", - "dispatchEvent", - ]); + webidl.requiredArguments(arguments.length, 2, { + prefix, + }); - class ErrorEvent extends Event { - #message = ""; - #filename = ""; - #lineno = ""; - #colno = ""; - #error = ""; + options = webidl.converters.AddEventListenerOptions(options, { + prefix, + context: "Argument 3", + }); - get message() { - return this.#message; - } - get filename() { - return this.#filename; + if (callback === null) { + return; } - get lineno() { - return this.#lineno; + + const { listeners } = self[eventTargetData]; + + if (!(ReflectHas(listeners, type))) { + listeners[type] = []; } - get colno() { - return this.#colno; + + const listenerList = listeners[type]; + for (let i = 0; i < listenerList.length; ++i) { + const listener = listenerList[i]; + if ( + ((typeof listener.options === "boolean" && + listener.options === options.capture) || + (typeof listener.options === "object" && + listener.options.capture === options.capture)) && + listener.callback === callback + ) { + return; + } } - get error() { - return this.#error; + if (options?.signal) { + const signal = options?.signal; + if (signal.aborted) { + // If signal is not null and its aborted flag is set, then return. + return; + } else { + // If listener’s signal is not null, then add the following abort + // abort steps to it: Remove an event listener. + signal.addEventListener("abort", () => { + self.removeEventListener(type, callback, options); + }); + } } - constructor( - type, - { - bubbles, - cancelable, - composed, - message = "", - filename = "", - lineno = 0, - colno = 0, - error, - } = {}, - ) { - super(type, { - bubbles: bubbles, - cancelable: cancelable, - composed: composed, - }); + ArrayPrototypePush(listeners[type], { callback, options }); + } - this.#message = message; - this.#filename = filename; - this.#lineno = lineno; - this.#colno = colno; - this.#error = error; - } + removeEventListener( + type, + callback, + options, + ) { + const self = this ?? globalThis_; + webidl.assertBranded(self, EventTargetPrototype); + webidl.requiredArguments(arguments.length, 2, { + prefix: "Failed to execute 'removeEventListener' on 'EventTarget'", + }); - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, this), - keys: [ - ...new SafeArrayIterator(EVENT_PROPS), - "message", - "filename", - "lineno", - "colno", - "error", - ], - })); + const { listeners } = self[eventTargetData]; + if (callback !== null && ReflectHas(listeners, type)) { + listeners[type] = ArrayPrototypeFilter( + listeners[type], + (listener) => listener.callback !== callback, + ); + } else if (callback === null || !listeners[type]) { + return; } - // TODO(lucacasonato): remove when this interface is spec aligned - [SymbolToStringTag] = "ErrorEvent"; - } + options = normalizeEventHandlerOptions(options); - defineEnumerableProps(ErrorEvent, [ - "message", - "filename", - "lineno", - "colno", - "error", - ]); + for (let i = 0; i < listeners[type].length; ++i) { + const listener = listeners[type][i]; + if ( + ((typeof listener.options === "boolean" && + listener.options === options.capture) || + (typeof listener.options === "object" && + listener.options.capture === options.capture)) && + listener.callback === callback + ) { + ArrayPrototypeSplice(listeners[type], i, 1); + break; + } + } + } - class CloseEvent extends Event { - #wasClean = ""; - #code = ""; - #reason = ""; + dispatchEvent(event) { + // If `this` is not present, then fallback to global scope. We don't use + // `globalThis` directly here, because it could be deleted by user. + // Instead use saved reference to global scope when the script was + // executed. + const self = this ?? globalThis_; + webidl.assertBranded(self, EventTargetPrototype); + webidl.requiredArguments(arguments.length, 1, { + prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'", + }); - get wasClean() { - return this.#wasClean; + const { listeners } = self[eventTargetData]; + if (!ReflectHas(listeners, event.type)) { + setTarget(event, this); + return true; } - get code() { - return this.#code; + + if (getDispatched(event)) { + throw new DOMException("Invalid event state.", "InvalidStateError"); } - get reason() { - return this.#reason; + + if (event.eventPhase !== Event.NONE) { + throw new DOMException("Invalid event state.", "InvalidStateError"); } - constructor(type, { + return dispatch(self, event); + } + + getParent(_event) { + return null; + } +} + +webidl.configurePrototype(EventTarget); +const EventTargetPrototype = EventTarget.prototype; + +defineEnumerableProps(EventTarget, [ + "addEventListener", + "removeEventListener", + "dispatchEvent", +]); + +class ErrorEvent extends Event { + #message = ""; + #filename = ""; + #lineno = ""; + #colno = ""; + #error = ""; + + get message() { + return this.#message; + } + get filename() { + return this.#filename; + } + get lineno() { + return this.#lineno; + } + get colno() { + return this.#colno; + } + get error() { + return this.#error; + } + + constructor( + type, + { bubbles, cancelable, composed, - wasClean = false, - code = 0, - reason = "", - } = {}) { - super(type, { - bubbles: bubbles, - cancelable: cancelable, - composed: composed, - }); + message = "", + filename = "", + lineno = 0, + colno = 0, + error, + } = {}, + ) { + super(type, { + bubbles: bubbles, + cancelable: cancelable, + composed: composed, + }); - this.#wasClean = wasClean; - this.#code = code; - this.#reason = reason; - } + this.#message = message; + this.#filename = filename; + this.#lineno = lineno; + this.#colno = colno; + this.#error = error; + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(CloseEvent.prototype, this), - keys: [ - ...new SafeArrayIterator(EVENT_PROPS), - "wasClean", - "code", - "reason", - ], - })); - } + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, this), + keys: [ + ...new SafeArrayIterator(EVENT_PROPS), + "message", + "filename", + "lineno", + "colno", + "error", + ], + })); } - class MessageEvent extends Event { - get source() { - return null; - } + // TODO(lucacasonato): remove when this interface is spec aligned + [SymbolToStringTag] = "ErrorEvent"; +} + +defineEnumerableProps(ErrorEvent, [ + "message", + "filename", + "lineno", + "colno", + "error", +]); + +class CloseEvent extends Event { + #wasClean = ""; + #code = ""; + #reason = ""; + + get wasClean() { + return this.#wasClean; + } + get code() { + return this.#code; + } + get reason() { + return this.#reason; + } - constructor(type, eventInitDict) { - super(type, { - bubbles: eventInitDict?.bubbles ?? false, - cancelable: eventInitDict?.cancelable ?? false, - composed: eventInitDict?.composed ?? false, - [_skipInternalInit]: eventInitDict?.[_skipInternalInit], - }); + constructor(type, { + bubbles, + cancelable, + composed, + wasClean = false, + code = 0, + reason = "", + } = {}) { + super(type, { + bubbles: bubbles, + cancelable: cancelable, + composed: composed, + }); - this.data = eventInitDict?.data ?? null; - this.ports = eventInitDict?.ports ?? []; - this.origin = eventInitDict?.origin ?? ""; - this.lastEventId = eventInitDict?.lastEventId ?? ""; - } + this.#wasClean = wasClean; + this.#code = code; + this.#reason = reason; + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(MessageEvent.prototype, this), - keys: [ - ...new SafeArrayIterator(EVENT_PROPS), - "data", - "origin", - "lastEventId", - ], - })); - } + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(CloseEvent.prototype, this), + keys: [ + ...new SafeArrayIterator(EVENT_PROPS), + "wasClean", + "code", + "reason", + ], + })); + } +} - // TODO(lucacasonato): remove when this interface is spec aligned - [SymbolToStringTag] = "CloseEvent"; +class MessageEvent extends Event { + get source() { + return null; } - class CustomEvent extends Event { - #detail = null; + constructor(type, eventInitDict) { + super(type, { + bubbles: eventInitDict?.bubbles ?? false, + cancelable: eventInitDict?.cancelable ?? false, + composed: eventInitDict?.composed ?? false, + [_skipInternalInit]: eventInitDict?.[_skipInternalInit], + }); - constructor(type, eventInitDict = {}) { - super(type, eventInitDict); - webidl.requiredArguments(arguments.length, 1, { - prefix: "Failed to construct 'CustomEvent'", - }); - const { detail } = eventInitDict; - this.#detail = detail; - } + this.data = eventInitDict?.data ?? null; + this.ports = eventInitDict?.ports ?? []; + this.origin = eventInitDict?.origin ?? ""; + this.lastEventId = eventInitDict?.lastEventId ?? ""; + } - get detail() { - return this.#detail; - } + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(MessageEvent.prototype, this), + keys: [ + ...new SafeArrayIterator(EVENT_PROPS), + "data", + "origin", + "lastEventId", + ], + })); + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(CustomEvent.prototype, this), - keys: [ - ...new SafeArrayIterator(EVENT_PROPS), - "detail", - ], - })); - } + // TODO(lucacasonato): remove when this interface is spec aligned + [SymbolToStringTag] = "CloseEvent"; +} - // TODO(lucacasonato): remove when this interface is spec aligned - [SymbolToStringTag] = "CustomEvent"; +class CustomEvent extends Event { + #detail = null; + + constructor(type, eventInitDict = {}) { + super(type, eventInitDict); + webidl.requiredArguments(arguments.length, 1, { + prefix: "Failed to construct 'CustomEvent'", + }); + const { detail } = eventInitDict; + this.#detail = detail; } - ReflectDefineProperty(CustomEvent.prototype, "detail", { - enumerable: true, - }); + get detail() { + return this.#detail; + } - // ProgressEvent could also be used in other DOM progress event emits. - // Current use is for FileReader. - class ProgressEvent extends Event { - constructor(type, eventInitDict = {}) { - super(type, eventInitDict); + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(CustomEvent.prototype, this), + keys: [ + ...new SafeArrayIterator(EVENT_PROPS), + "detail", + ], + })); + } - this.lengthComputable = eventInitDict?.lengthComputable ?? false; - this.loaded = eventInitDict?.loaded ?? 0; - this.total = eventInitDict?.total ?? 0; - } + // TODO(lucacasonato): remove when this interface is spec aligned + [SymbolToStringTag] = "CustomEvent"; +} - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(ProgressEvent.prototype, this), - keys: [ - ...new SafeArrayIterator(EVENT_PROPS), - "lengthComputable", - "loaded", - "total", - ], - })); - } +ReflectDefineProperty(CustomEvent.prototype, "detail", { + enumerable: true, +}); - // TODO(lucacasonato): remove when this interface is spec aligned - [SymbolToStringTag] = "ProgressEvent"; +// ProgressEvent could also be used in other DOM progress event emits. +// Current use is for FileReader. +class ProgressEvent extends Event { + constructor(type, eventInitDict = {}) { + super(type, eventInitDict); + + this.lengthComputable = eventInitDict?.lengthComputable ?? false; + this.loaded = eventInitDict?.loaded ?? 0; + this.total = eventInitDict?.total ?? 0; } - class PromiseRejectionEvent extends Event { - #promise = null; - #reason = null; + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(ProgressEvent.prototype, this), + keys: [ + ...new SafeArrayIterator(EVENT_PROPS), + "lengthComputable", + "loaded", + "total", + ], + })); + } - get promise() { - return this.#promise; - } - get reason() { - return this.#reason; - } + // TODO(lucacasonato): remove when this interface is spec aligned + [SymbolToStringTag] = "ProgressEvent"; +} - constructor( - type, - { - bubbles, - cancelable, - composed, - promise, - reason, - } = {}, - ) { - super(type, { - bubbles: bubbles, - cancelable: cancelable, - composed: composed, - }); +class PromiseRejectionEvent extends Event { + #promise = null; + #reason = null; - this.#promise = promise; - this.#reason = reason; - } + get promise() { + return this.#promise; + } + get reason() { + return this.#reason; + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - PromiseRejectionEvent.prototype, - this, - ), - keys: [ - ...new SafeArrayIterator(EVENT_PROPS), - "promise", - "reason", - ], - })); - } + constructor( + type, + { + bubbles, + cancelable, + composed, + promise, + reason, + } = {}, + ) { + super(type, { + bubbles: bubbles, + cancelable: cancelable, + composed: composed, + }); - // TODO(lucacasonato): remove when this interface is spec aligned - [SymbolToStringTag] = "PromiseRejectionEvent"; + this.#promise = promise; + this.#reason = reason; } - defineEnumerableProps(PromiseRejectionEvent, [ - "promise", - "reason", - ]); + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + PromiseRejectionEvent.prototype, + this, + ), + keys: [ + ...new SafeArrayIterator(EVENT_PROPS), + "promise", + "reason", + ], + })); + } - const _eventHandlers = Symbol("eventHandlers"); + // TODO(lucacasonato): remove when this interface is spec aligned + [SymbolToStringTag] = "PromiseRejectionEvent"; +} - function makeWrappedHandler(handler, isSpecialErrorEventHandler) { - function wrappedHandler(evt) { - if (typeof wrappedHandler.handler !== "function") { - return; - } +defineEnumerableProps(PromiseRejectionEvent, [ + "promise", + "reason", +]); - if ( - isSpecialErrorEventHandler && - ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, evt) && - evt.type === "error" - ) { - const ret = FunctionPrototypeCall( - wrappedHandler.handler, - this, - evt.message, - evt.filename, - evt.lineno, - evt.colno, - evt.error, - ); - if (ret === true) { - evt.preventDefault(); - } - return; - } +const _eventHandlers = Symbol("eventHandlers"); - return FunctionPrototypeCall(wrappedHandler.handler, this, evt); +function makeWrappedHandler(handler, isSpecialErrorEventHandler) { + function wrappedHandler(evt) { + if (typeof wrappedHandler.handler !== "function") { + return; } - wrappedHandler.handler = handler; - return wrappedHandler; - } - - // `init` is an optional function that will be called the first time that the - // event handler property is set. It will be called with the object on which - // the property is set as its argument. - // `isSpecialErrorEventHandler` can be set to true to opt into the special - // behavior of event handlers for the "error" event in a global scope. - function defineEventHandler( - emitter, - name, - init = undefined, - isSpecialErrorEventHandler = false, - ) { - // HTML specification section 8.1.7.1 - ObjectDefineProperty(emitter, `on${name}`, { - get() { - if (!this[_eventHandlers]) { - return null; - } - return MapPrototypeGet(this[_eventHandlers], name)?.handler ?? null; - }, - set(value) { - // All three Web IDL event handler types are nullable callback functions - // with the [LegacyTreatNonObjectAsNull] extended attribute, meaning - // anything other than an object is treated as null. - if (typeof value !== "object" && typeof value !== "function") { - value = null; - } + if ( + isSpecialErrorEventHandler && + ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, evt) && + evt.type === "error" + ) { + const ret = FunctionPrototypeCall( + wrappedHandler.handler, + this, + evt.message, + evt.filename, + evt.lineno, + evt.colno, + evt.error, + ); + if (ret === true) { + evt.preventDefault(); + } + return; + } - if (!this[_eventHandlers]) { - this[_eventHandlers] = new Map(); - } - let handlerWrapper = MapPrototypeGet(this[_eventHandlers], name); - if (handlerWrapper) { - handlerWrapper.handler = value; - } else if (value !== null) { - handlerWrapper = makeWrappedHandler( - value, - isSpecialErrorEventHandler, - ); - this.addEventListener(name, handlerWrapper); - init?.(this); - } - MapPrototypeSet(this[_eventHandlers], name, handlerWrapper); - }, - configurable: true, - enumerable: true, - }); + return FunctionPrototypeCall(wrappedHandler.handler, this, evt); } + wrappedHandler.handler = handler; + return wrappedHandler; +} + +// `init` is an optional function that will be called the first time that the +// event handler property is set. It will be called with the object on which +// the property is set as its argument. +// `isSpecialErrorEventHandler` can be set to true to opt into the special +// behavior of event handlers for the "error" event in a global scope. +function defineEventHandler( + emitter, + name, + init = undefined, + isSpecialErrorEventHandler = false, +) { + // HTML specification section 8.1.7.1 + ObjectDefineProperty(emitter, `on${name}`, { + get() { + if (!this[_eventHandlers]) { + return null; + } - let reportExceptionStackedCalls = 0; - - // https://html.spec.whatwg.org/#report-the-exception - function reportException(error) { - reportExceptionStackedCalls++; - const jsError = core.destructureError(error); - const message = jsError.exceptionMessage; - let filename = ""; - let lineno = 0; - let colno = 0; - if (jsError.frames.length > 0) { - filename = jsError.frames[0].fileName; - lineno = jsError.frames[0].lineNumber; - colno = jsError.frames[0].columnNumber; - } else { - const jsError = core.destructureError(new Error()); - const frames = jsError.frames; - for (let i = 0; i < frames.length; ++i) { - const frame = frames[i]; - if ( - typeof frame.fileName == "string" && - !StringPrototypeStartsWith(frame.fileName, "internal:") - ) { - filename = frame.fileName; - lineno = frame.lineNumber; - colno = frame.columnNumber; - break; - } + return MapPrototypeGet(this[_eventHandlers], name)?.handler ?? null; + }, + set(value) { + // All three Web IDL event handler types are nullable callback functions + // with the [LegacyTreatNonObjectAsNull] extended attribute, meaning + // anything other than an object is treated as null. + if (typeof value !== "object" && typeof value !== "function") { + value = null; } - } - const event = new ErrorEvent("error", { - cancelable: true, - message, - filename, - lineno, - colno, - error, - }); - // Avoid recursing `reportException()` via error handlers more than once. - if (reportExceptionStackedCalls > 1 || window.dispatchEvent(event)) { - ops.op_dispatch_exception(error); - } - reportExceptionStackedCalls--; - } - function checkThis(thisArg) { - if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) { - throw new TypeError("Illegal invocation"); + if (!this[_eventHandlers]) { + this[_eventHandlers] = new Map(); + } + let handlerWrapper = MapPrototypeGet(this[_eventHandlers], name); + if (handlerWrapper) { + handlerWrapper.handler = value; + } else if (value !== null) { + handlerWrapper = makeWrappedHandler( + value, + isSpecialErrorEventHandler, + ); + this.addEventListener(name, handlerWrapper); + init?.(this); + } + MapPrototypeSet(this[_eventHandlers], name, handlerWrapper); + }, + configurable: true, + enumerable: true, + }); +} + +let reportExceptionStackedCalls = 0; + +// https://html.spec.whatwg.org/#report-the-exception +function reportException(error) { + reportExceptionStackedCalls++; + const jsError = core.destructureError(error); + const message = jsError.exceptionMessage; + let filename = ""; + let lineno = 0; + let colno = 0; + if (jsError.frames.length > 0) { + filename = jsError.frames[0].fileName; + lineno = jsError.frames[0].lineNumber; + colno = jsError.frames[0].columnNumber; + } else { + const jsError = core.destructureError(new Error()); + const frames = jsError.frames; + for (let i = 0; i < frames.length; ++i) { + const frame = frames[i]; + if ( + typeof frame.fileName == "string" && + !StringPrototypeStartsWith(frame.fileName, "internal:") + ) { + filename = frame.fileName; + lineno = frame.lineNumber; + colno = frame.columnNumber; + break; + } } } - - // https://html.spec.whatwg.org/#dom-reporterror - function reportError(error) { - checkThis(this); - const prefix = "Failed to call 'reportError'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - reportException(error); + const event = new ErrorEvent("error", { + cancelable: true, + message, + filename, + lineno, + colno, + error, + }); + // Avoid recursing `reportException()` via error handlers more than once. + if (reportExceptionStackedCalls > 1 || globalThis_.dispatchEvent(event)) { + ops.op_dispatch_exception(error); } + reportExceptionStackedCalls--; +} - window.__bootstrap.eventTarget = { - EventTarget, - setEventTargetData, - listenerCount, - }; - window.__bootstrap.event = { - reportException, - setIsTrusted, - setTarget, - defineEventHandler, - _skipInternalInit, - Event, - ErrorEvent, - CloseEvent, - MessageEvent, - CustomEvent, - ProgressEvent, - PromiseRejectionEvent, - reportError, - }; -})(this); +function checkThis(thisArg) { + if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis_) { + throw new TypeError("Illegal invocation"); + } +} + +// https://html.spec.whatwg.org/#dom-reporterror +function reportError(error) { + checkThis(this); + const prefix = "Failed to call 'reportError'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + reportException(error); +} + +export { + _skipInternalInit, + CloseEvent, + CustomEvent, + defineEventHandler, + ErrorEvent, + Event, + EventTarget, + listenerCount, + MessageEvent, + ProgressEvent, + PromiseRejectionEvent, + reportError, + reportException, + saveGlobalThisReference, + setEventTargetData, + setIsTrusted, + setTarget, +}; diff --git a/ext/web/02_structured_clone.js b/ext/web/02_structured_clone.js index 793cb1c75..373ae0ab2 100644 --- a/ext/web/02_structured_clone.js +++ b/ext/web/02_structured_clone.js @@ -6,138 +6,135 @@ /// <reference path="../web/internal.d.ts" /> /// <reference path="../web/lib.deno_web.d.ts" /> -"use strict"; +const core = globalThis.Deno.core; +import DOMException from "internal:ext/web/01_dom_exception.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayBuffer, + ArrayBufferPrototype, + ArrayBufferPrototypeGetByteLength, + ArrayBufferPrototypeSlice, + ArrayBufferIsView, + DataView, + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, + ObjectPrototypeIsPrototypeOf, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteOffset, + TypedArrayPrototypeGetLength, + TypedArrayPrototypeGetSymbolToStringTag, + TypeErrorPrototype, + WeakMap, + WeakMapPrototypeSet, + Int8Array, + Int16Array, + Int32Array, + BigInt64Array, + Uint8Array, + Uint8ClampedArray, + Uint16Array, + Uint32Array, + BigUint64Array, + Float32Array, + Float64Array, +} = primordials; -((window) => { - const core = window.Deno.core; - const { DOMException } = window.__bootstrap.domException; - const { - ArrayBuffer, - ArrayBufferPrototype, - ArrayBufferPrototypeGetByteLength, - ArrayBufferPrototypeSlice, - ArrayBufferIsView, - DataView, - DataViewPrototypeGetBuffer, - DataViewPrototypeGetByteLength, - DataViewPrototypeGetByteOffset, - ObjectPrototypeIsPrototypeOf, - TypedArrayPrototypeGetBuffer, - TypedArrayPrototypeGetByteOffset, - TypedArrayPrototypeGetLength, - TypedArrayPrototypeGetSymbolToStringTag, - TypeErrorPrototype, - WeakMap, - WeakMapPrototypeSet, - Int8Array, - Int16Array, - Int32Array, - BigInt64Array, - Uint8Array, - Uint8ClampedArray, - Uint16Array, - Uint32Array, - BigUint64Array, - Float32Array, - Float64Array, - } = window.__bootstrap.primordials; +const objectCloneMemo = new WeakMap(); - const objectCloneMemo = new WeakMap(); - - function cloneArrayBuffer( +function cloneArrayBuffer( + srcBuffer, + srcByteOffset, + srcLength, + _cloneConstructor, +) { + // this function fudges the return type but SharedArrayBuffer is disabled for a while anyway + return ArrayBufferPrototypeSlice( srcBuffer, srcByteOffset, - srcLength, - _cloneConstructor, - ) { - // this function fudges the return type but SharedArrayBuffer is disabled for a while anyway - return ArrayBufferPrototypeSlice( - srcBuffer, - srcByteOffset, - srcByteOffset + srcLength, + srcByteOffset + srcLength, + ); +} + +// TODO(petamoriken): Resizable ArrayBuffer support in the future +/** Clone a value in a similar way to structured cloning. It is similar to a + * StructureDeserialize(StructuredSerialize(...)). */ +function structuredClone(value) { + // Performance optimization for buffers, otherwise + // `serialize/deserialize` will allocate new buffer. + if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, value)) { + const cloned = cloneArrayBuffer( + value, + 0, + ArrayBufferPrototypeGetByteLength(value), + ArrayBuffer, ); + WeakMapPrototypeSet(objectCloneMemo, value, cloned); + return cloned; } - // TODO(petamoriken): Resizable ArrayBuffer support in the future - /** Clone a value in a similar way to structured cloning. It is similar to a - * StructureDeserialize(StructuredSerialize(...)). */ - function structuredClone(value) { - // Performance optimization for buffers, otherwise - // `serialize/deserialize` will allocate new buffer. - if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, value)) { - const cloned = cloneArrayBuffer( - value, - 0, - ArrayBufferPrototypeGetByteLength(value), - ArrayBuffer, + if (ArrayBufferIsView(value)) { + const tag = TypedArrayPrototypeGetSymbolToStringTag(value); + // DataView + if (tag === undefined) { + return new DataView( + structuredClone(DataViewPrototypeGetBuffer(value)), + DataViewPrototypeGetByteOffset(value), + DataViewPrototypeGetByteLength(value), ); - WeakMapPrototypeSet(objectCloneMemo, value, cloned); - return cloned; } - - if (ArrayBufferIsView(value)) { - const tag = TypedArrayPrototypeGetSymbolToStringTag(value); - // DataView - if (tag === undefined) { - return new DataView( - structuredClone(DataViewPrototypeGetBuffer(value)), - DataViewPrototypeGetByteOffset(value), - DataViewPrototypeGetByteLength(value), - ); - } - // TypedArray - let Constructor; - switch (tag) { - case "Int8Array": - Constructor = Int8Array; - break; - case "Int16Array": - Constructor = Int16Array; - break; - case "Int32Array": - Constructor = Int32Array; - break; - case "BigInt64Array": - Constructor = BigInt64Array; - break; - case "Uint8Array": - Constructor = Uint8Array; - break; - case "Uint8ClampedArray": - Constructor = Uint8ClampedArray; - break; - case "Uint16Array": - Constructor = Uint16Array; - break; - case "Uint32Array": - Constructor = Uint32Array; - break; - case "BigUint64Array": - Constructor = BigUint64Array; - break; - case "Float32Array": - Constructor = Float32Array; - break; - case "Float64Array": - Constructor = Float64Array; - break; - } - return new Constructor( - structuredClone(TypedArrayPrototypeGetBuffer(value)), - TypedArrayPrototypeGetByteOffset(value), - TypedArrayPrototypeGetLength(value), - ); + // TypedArray + let Constructor; + switch (tag) { + case "Int8Array": + Constructor = Int8Array; + break; + case "Int16Array": + Constructor = Int16Array; + break; + case "Int32Array": + Constructor = Int32Array; + break; + case "BigInt64Array": + Constructor = BigInt64Array; + break; + case "Uint8Array": + Constructor = Uint8Array; + break; + case "Uint8ClampedArray": + Constructor = Uint8ClampedArray; + break; + case "Uint16Array": + Constructor = Uint16Array; + break; + case "Uint32Array": + Constructor = Uint32Array; + break; + case "BigUint64Array": + Constructor = BigUint64Array; + break; + case "Float32Array": + Constructor = Float32Array; + break; + case "Float64Array": + Constructor = Float64Array; + break; } + return new Constructor( + structuredClone(TypedArrayPrototypeGetBuffer(value)), + TypedArrayPrototypeGetByteOffset(value), + TypedArrayPrototypeGetLength(value), + ); + } - try { - return core.deserialize(core.serialize(value)); - } catch (e) { - if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) { - throw new DOMException(e.message, "DataCloneError"); - } - throw e; + try { + return core.deserialize(core.serialize(value)); + } catch (e) { + if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) { + throw new DOMException(e.message, "DataCloneError"); } + throw e; } +} - window.__bootstrap.structuredClone = structuredClone; -})(globalThis); +export { structuredClone }; diff --git a/ext/web/02_timers.js b/ext/web/02_timers.js index a582cf428..302b6f62c 100644 --- a/ext/web/02_timers.js +++ b/ext/web/02_timers.js @@ -1,375 +1,372 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; - -((window) => { - const core = window.Deno.core; - const ops = core.ops; - const { - ArrayPrototypePush, - ArrayPrototypeShift, - FunctionPrototypeCall, - Map, - MapPrototypeDelete, - MapPrototypeGet, - MapPrototypeHas, - MapPrototypeSet, - Uint8Array, - Uint32Array, - // deno-lint-ignore camelcase - NumberPOSITIVE_INFINITY, - PromisePrototypeThen, - SafeArrayIterator, - SymbolFor, - TypeError, - indirectEval, - } = window.__bootstrap.primordials; - const { webidl } = window.__bootstrap; - const { reportException } = window.__bootstrap.event; - const { assert } = window.__bootstrap.infra; - - const hrU8 = new Uint8Array(8); - const hr = new Uint32Array(hrU8.buffer); - function opNow() { - ops.op_now(hrU8); - return (hr[0] * 1000 + hr[1] / 1e6); - } - // --------------------------------------------------------------------------- - - /** - * The task queue corresponding to the timer task source. - * - * @type { {action: () => void, nestingLevel: number}[] } - */ - const timerTasks = []; - - /** - * The current task's timer nesting level, or zero if we're not currently - * running a timer task (since the minimum nesting level is 1). - * - * @type {number} - */ - let timerNestingLevel = 0; - - function handleTimerMacrotask() { - if (timerTasks.length === 0) { - return true; - } - - const task = ArrayPrototypeShift(timerTasks); - - timerNestingLevel = task.nestingLevel; - - try { - task.action(); - } finally { - timerNestingLevel = 0; - } - return timerTasks.length === 0; +const core = globalThis.Deno.core; +const ops = core.ops; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypePush, + ArrayPrototypeShift, + FunctionPrototypeCall, + Map, + MapPrototypeDelete, + MapPrototypeGet, + MapPrototypeHas, + MapPrototypeSet, + Uint8Array, + Uint32Array, + // deno-lint-ignore camelcase + NumberPOSITIVE_INFINITY, + PromisePrototypeThen, + SafeArrayIterator, + SymbolFor, + TypeError, + indirectEval, +} = primordials; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { reportException } from "internal:ext/web/02_event.js"; +import { assert } from "internal:ext/web/00_infra.js"; + +const hrU8 = new Uint8Array(8); +const hr = new Uint32Array(hrU8.buffer); +function opNow() { + ops.op_now(hrU8); + return (hr[0] * 1000 + hr[1] / 1e6); +} + +// --------------------------------------------------------------------------- + +/** + * The task queue corresponding to the timer task source. + * + * @type { {action: () => void, nestingLevel: number}[] } + */ +const timerTasks = []; + +/** + * The current task's timer nesting level, or zero if we're not currently + * running a timer task (since the minimum nesting level is 1). + * + * @type {number} + */ +let timerNestingLevel = 0; + +function handleTimerMacrotask() { + if (timerTasks.length === 0) { + return true; } - // --------------------------------------------------------------------------- - - /** - * The keys in this map correspond to the key ID's in the spec's map of active - * timers. The values are the timeout's cancel rid. - * - * @type {Map<number, { cancelRid: number, isRef: boolean, promiseId: number }>} - */ - const activeTimers = new Map(); - - let nextId = 1; - - /** - * @param {Function | string} callback - * @param {number} timeout - * @param {Array<any>} args - * @param {boolean} repeat - * @param {number | undefined} prevId - * @returns {number} The timer ID - */ - function initializeTimer( - callback, - timeout, - args, - repeat, - prevId, - ) { - // 2. If previousId was given, let id be previousId; otherwise, let - // previousId be an implementation-defined integer than is greater than zero - // and does not already exist in global's map of active timers. - let id; - let timerInfo; - if (prevId !== undefined) { - // `prevId` is only passed for follow-up calls on intervals - assert(repeat); - id = prevId; - timerInfo = MapPrototypeGet(activeTimers, id); - } else { - // TODO(@andreubotella): Deal with overflow. - // https://github.com/whatwg/html/issues/7358 - id = nextId++; - const cancelRid = ops.op_timer_handle(); - timerInfo = { cancelRid, isRef: true, promiseId: -1 }; - - // Step 4 in "run steps after a timeout". - MapPrototypeSet(activeTimers, id, timerInfo); - } - - // 3. If the surrounding agent's event loop's currently running task is a - // task that was created by this algorithm, then let nesting level be the - // task's timer nesting level. Otherwise, let nesting level be zero. - // 4. If timeout is less than 0, then set timeout to 0. - // 5. If nesting level is greater than 5, and timeout is less than 4, then - // set timeout to 4. - // - // The nesting level of 5 and minimum of 4 ms are spec-mandated magic - // constants. - if (timeout < 0) timeout = 0; - if (timerNestingLevel > 5 && timeout < 4) timeout = 4; - - // 9. Let task be a task that runs the following steps: - const task = { - action: () => { - // 1. If id does not exist in global's map of active timers, then abort - // these steps. - // - // This is relevant if the timer has been canceled after the sleep op - // resolves but before this task runs. - if (!MapPrototypeHas(activeTimers, id)) { - return; - } + const task = ArrayPrototypeShift(timerTasks); - // 2. - // 3. - if (typeof callback === "function") { - try { - FunctionPrototypeCall( - callback, - globalThis, - ...new SafeArrayIterator(args), - ); - } catch (error) { - reportException(error); - } - } else { - indirectEval(callback); - } + timerNestingLevel = task.nestingLevel; - if (repeat) { - if (MapPrototypeHas(activeTimers, id)) { - // 4. If id does not exist in global's map of active timers, then - // abort these steps. - // NOTE: If might have been removed via the author code in handler - // calling clearTimeout() or clearInterval(). - // 5. If repeat is true, then perform the timer initialization steps - // again, given global, handler, timeout, arguments, true, and id. - initializeTimer(callback, timeout, args, true, id); - } - } else { - // 6. Otherwise, remove global's map of active timers[id]. - core.tryClose(timerInfo.cancelRid); - MapPrototypeDelete(activeTimers, id); - } - }, - - // 10. Increment nesting level by one. - // 11. Set task's timer nesting level to nesting level. - nestingLevel: timerNestingLevel + 1, - }; - - // 12. Let completionStep be an algorithm step which queues a global task on - // the timer task source given global to run task. - // 13. Run steps after a timeout given global, "setTimeout/setInterval", - // timeout, completionStep, and id. - runAfterTimeout( - () => ArrayPrototypePush(timerTasks, task), - timeout, - timerInfo, - ); - - return id; + try { + task.action(); + } finally { + timerNestingLevel = 0; + } + return timerTasks.length === 0; +} + +// --------------------------------------------------------------------------- + +/** + * The keys in this map correspond to the key ID's in the spec's map of active + * timers. The values are the timeout's cancel rid. + * + * @type {Map<number, { cancelRid: number, isRef: boolean, promiseId: number }>} + */ +const activeTimers = new Map(); + +let nextId = 1; + +/** + * @param {Function | string} callback + * @param {number} timeout + * @param {Array<any>} args + * @param {boolean} repeat + * @param {number | undefined} prevId + * @returns {number} The timer ID + */ +function initializeTimer( + callback, + timeout, + args, + repeat, + prevId, +) { + // 2. If previousId was given, let id be previousId; otherwise, let + // previousId be an implementation-defined integer than is greater than zero + // and does not already exist in global's map of active timers. + let id; + let timerInfo; + if (prevId !== undefined) { + // `prevId` is only passed for follow-up calls on intervals + assert(repeat); + id = prevId; + timerInfo = MapPrototypeGet(activeTimers, id); + } else { + // TODO(@andreubotella): Deal with overflow. + // https://github.com/whatwg/html/issues/7358 + id = nextId++; + const cancelRid = ops.op_timer_handle(); + timerInfo = { cancelRid, isRef: true, promiseId: -1 }; + + // Step 4 in "run steps after a timeout". + MapPrototypeSet(activeTimers, id, timerInfo); } - // --------------------------------------------------------------------------- - - /** - * @typedef ScheduledTimer - * @property {number} millis - * @property {() => void} cb - * @property {boolean} resolved - * @property {ScheduledTimer | null} prev - * @property {ScheduledTimer | null} next - */ - - /** - * A doubly linked list of timers. - * @type { { head: ScheduledTimer | null, tail: ScheduledTimer | null } } - */ - const scheduledTimers = { head: null, tail: null }; - - /** - * @param {() => void} cb Will be run after the timeout, if it hasn't been - * cancelled. - * @param {number} millis - * @param {{ cancelRid: number, isRef: boolean, promiseId: number }} timerInfo - */ - function runAfterTimeout(cb, millis, timerInfo) { - const cancelRid = timerInfo.cancelRid; - const sleepPromise = core.opAsync("op_sleep", millis, cancelRid); - timerInfo.promiseId = - sleepPromise[SymbolFor("Deno.core.internalPromiseId")]; - if (!timerInfo.isRef) { - core.unrefOp(timerInfo.promiseId); - } - - /** @type {ScheduledTimer} */ - const timerObject = { - millis, - cb, - resolved: false, - prev: scheduledTimers.tail, - next: null, - }; - - // Add timerObject to the end of the list. - if (scheduledTimers.tail === null) { - assert(scheduledTimers.head === null); - scheduledTimers.head = scheduledTimers.tail = timerObject; - } else { - scheduledTimers.tail.next = timerObject; - scheduledTimers.tail = timerObject; - } - - // 1. - PromisePrototypeThen( - sleepPromise, - (cancelled) => { - if (!cancelled) { - // The timer was cancelled. - removeFromScheduledTimers(timerObject); - return; + // 3. If the surrounding agent's event loop's currently running task is a + // task that was created by this algorithm, then let nesting level be the + // task's timer nesting level. Otherwise, let nesting level be zero. + // 4. If timeout is less than 0, then set timeout to 0. + // 5. If nesting level is greater than 5, and timeout is less than 4, then + // set timeout to 4. + // + // The nesting level of 5 and minimum of 4 ms are spec-mandated magic + // constants. + if (timeout < 0) timeout = 0; + if (timerNestingLevel > 5 && timeout < 4) timeout = 4; + + // 9. Let task be a task that runs the following steps: + const task = { + action: () => { + // 1. If id does not exist in global's map of active timers, then abort + // these steps. + // + // This is relevant if the timer has been canceled after the sleep op + // resolves but before this task runs. + if (!MapPrototypeHas(activeTimers, id)) { + return; + } + + // 2. + // 3. + if (typeof callback === "function") { + try { + FunctionPrototypeCall( + callback, + globalThis, + ...new SafeArrayIterator(args), + ); + } catch (error) { + reportException(error); } - // 2. Wait until any invocations of this algorithm that had the same - // global and orderingIdentifier, that started before this one, and - // whose milliseconds is equal to or less than this one's, have - // completed. - // 4. Perform completionSteps. - - // IMPORTANT: Since the sleep ops aren't guaranteed to resolve in the - // right order, whenever one resolves, we run through the scheduled - // timers list (which is in the order in which they were scheduled), and - // we call the callback for every timer which both: - // a) has resolved, and - // b) its timeout is lower than the lowest unresolved timeout found so - // far in the list. - - timerObject.resolved = true; - - let lowestUnresolvedTimeout = NumberPOSITIVE_INFINITY; - - let currentEntry = scheduledTimers.head; - while (currentEntry !== null) { - if (currentEntry.millis < lowestUnresolvedTimeout) { - if (currentEntry.resolved) { - currentEntry.cb(); - removeFromScheduledTimers(currentEntry); - } else { - lowestUnresolvedTimeout = currentEntry.millis; - } - } - - currentEntry = currentEntry.next; + } else { + indirectEval(callback); + } + + if (repeat) { + if (MapPrototypeHas(activeTimers, id)) { + // 4. If id does not exist in global's map of active timers, then + // abort these steps. + // NOTE: If might have been removed via the author code in handler + // calling clearTimeout() or clearInterval(). + // 5. If repeat is true, then perform the timer initialization steps + // again, given global, handler, timeout, arguments, true, and id. + initializeTimer(callback, timeout, args, true, id); } - }, - ); - } + } else { + // 6. Otherwise, remove global's map of active timers[id]. + core.tryClose(timerInfo.cancelRid); + MapPrototypeDelete(activeTimers, id); + } + }, + + // 10. Increment nesting level by one. + // 11. Set task's timer nesting level to nesting level. + nestingLevel: timerNestingLevel + 1, + }; - /** @param {ScheduledTimer} timerObj */ - function removeFromScheduledTimers(timerObj) { - if (timerObj.prev !== null) { - timerObj.prev.next = timerObj.next; - } else { - assert(scheduledTimers.head === timerObj); - scheduledTimers.head = timerObj.next; - } - if (timerObj.next !== null) { - timerObj.next.prev = timerObj.prev; - } else { - assert(scheduledTimers.tail === timerObj); - scheduledTimers.tail = timerObj.prev; - } + // 12. Let completionStep be an algorithm step which queues a global task on + // the timer task source given global to run task. + // 13. Run steps after a timeout given global, "setTimeout/setInterval", + // timeout, completionStep, and id. + runAfterTimeout( + () => ArrayPrototypePush(timerTasks, task), + timeout, + timerInfo, + ); + + return id; +} + +// --------------------------------------------------------------------------- + +/** + * @typedef ScheduledTimer + * @property {number} millis + * @property {() => void} cb + * @property {boolean} resolved + * @property {ScheduledTimer | null} prev + * @property {ScheduledTimer | null} next + */ + +/** + * A doubly linked list of timers. + * @type { { head: ScheduledTimer | null, tail: ScheduledTimer | null } } + */ +const scheduledTimers = { head: null, tail: null }; + +/** + * @param {() => void} cb Will be run after the timeout, if it hasn't been + * cancelled. + * @param {number} millis + * @param {{ cancelRid: number, isRef: boolean, promiseId: number }} timerInfo + */ +function runAfterTimeout(cb, millis, timerInfo) { + const cancelRid = timerInfo.cancelRid; + const sleepPromise = core.opAsync("op_sleep", millis, cancelRid); + timerInfo.promiseId = sleepPromise[SymbolFor("Deno.core.internalPromiseId")]; + if (!timerInfo.isRef) { + core.unrefOp(timerInfo.promiseId); } - // --------------------------------------------------------------------------- + /** @type {ScheduledTimer} */ + const timerObject = { + millis, + cb, + resolved: false, + prev: scheduledTimers.tail, + next: null, + }; - function checkThis(thisArg) { - if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) { - throw new TypeError("Illegal invocation"); - } + // Add timerObject to the end of the list. + if (scheduledTimers.tail === null) { + assert(scheduledTimers.head === null); + scheduledTimers.head = scheduledTimers.tail = timerObject; + } else { + scheduledTimers.tail.next = timerObject; + scheduledTimers.tail = timerObject; } - function setTimeout(callback, timeout = 0, ...args) { - checkThis(this); - if (typeof callback !== "function") { - callback = webidl.converters.DOMString(callback); - } - timeout = webidl.converters.long(timeout); + // 1. + PromisePrototypeThen( + sleepPromise, + (cancelled) => { + if (!cancelled) { + // The timer was cancelled. + removeFromScheduledTimers(timerObject); + return; + } + // 2. Wait until any invocations of this algorithm that had the same + // global and orderingIdentifier, that started before this one, and + // whose milliseconds is equal to or less than this one's, have + // completed. + // 4. Perform completionSteps. + + // IMPORTANT: Since the sleep ops aren't guaranteed to resolve in the + // right order, whenever one resolves, we run through the scheduled + // timers list (which is in the order in which they were scheduled), and + // we call the callback for every timer which both: + // a) has resolved, and + // b) its timeout is lower than the lowest unresolved timeout found so + // far in the list. + + timerObject.resolved = true; + + let lowestUnresolvedTimeout = NumberPOSITIVE_INFINITY; + + let currentEntry = scheduledTimers.head; + while (currentEntry !== null) { + if (currentEntry.millis < lowestUnresolvedTimeout) { + if (currentEntry.resolved) { + currentEntry.cb(); + removeFromScheduledTimers(currentEntry); + } else { + lowestUnresolvedTimeout = currentEntry.millis; + } + } - return initializeTimer(callback, timeout, args, false); + currentEntry = currentEntry.next; + } + }, + ); +} + +/** @param {ScheduledTimer} timerObj */ +function removeFromScheduledTimers(timerObj) { + if (timerObj.prev !== null) { + timerObj.prev.next = timerObj.next; + } else { + assert(scheduledTimers.head === timerObj); + scheduledTimers.head = timerObj.next; + } + if (timerObj.next !== null) { + timerObj.next.prev = timerObj.prev; + } else { + assert(scheduledTimers.tail === timerObj); + scheduledTimers.tail = timerObj.prev; } +} - function setInterval(callback, timeout = 0, ...args) { - checkThis(this); - if (typeof callback !== "function") { - callback = webidl.converters.DOMString(callback); - } - timeout = webidl.converters.long(timeout); +// --------------------------------------------------------------------------- - return initializeTimer(callback, timeout, args, true); +function checkThis(thisArg) { + if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) { + throw new TypeError("Illegal invocation"); } +} - function clearTimeout(id = 0) { - checkThis(this); - id = webidl.converters.long(id); - const timerInfo = MapPrototypeGet(activeTimers, id); - if (timerInfo !== undefined) { - core.tryClose(timerInfo.cancelRid); - MapPrototypeDelete(activeTimers, id); - } +function setTimeout(callback, timeout = 0, ...args) { + checkThis(this); + if (typeof callback !== "function") { + callback = webidl.converters.DOMString(callback); } + timeout = webidl.converters.long(timeout); - function clearInterval(id = 0) { - checkThis(this); - clearTimeout(id); - } + return initializeTimer(callback, timeout, args, false); +} - function refTimer(id) { - const timerInfo = MapPrototypeGet(activeTimers, id); - if (timerInfo === undefined || timerInfo.isRef) { - return; - } - timerInfo.isRef = true; - core.refOp(timerInfo.promiseId); +function setInterval(callback, timeout = 0, ...args) { + checkThis(this); + if (typeof callback !== "function") { + callback = webidl.converters.DOMString(callback); } - - function unrefTimer(id) { - const timerInfo = MapPrototypeGet(activeTimers, id); - if (timerInfo === undefined || !timerInfo.isRef) { - return; - } - timerInfo.isRef = false; - core.unrefOp(timerInfo.promiseId); + timeout = webidl.converters.long(timeout); + + return initializeTimer(callback, timeout, args, true); +} + +function clearTimeout(id = 0) { + checkThis(this); + id = webidl.converters.long(id); + const timerInfo = MapPrototypeGet(activeTimers, id); + if (timerInfo !== undefined) { + core.tryClose(timerInfo.cancelRid); + MapPrototypeDelete(activeTimers, id); } +} - window.__bootstrap.timers = { - setTimeout, - setInterval, - clearTimeout, - clearInterval, - handleTimerMacrotask, - opNow, - refTimer, - unrefTimer, - }; -})(this); +function clearInterval(id = 0) { + checkThis(this); + clearTimeout(id); +} + +function refTimer(id) { + const timerInfo = MapPrototypeGet(activeTimers, id); + if (timerInfo === undefined || timerInfo.isRef) { + return; + } + timerInfo.isRef = true; + core.refOp(timerInfo.promiseId); +} + +function unrefTimer(id) { + const timerInfo = MapPrototypeGet(activeTimers, id); + if (timerInfo === undefined || !timerInfo.isRef) { + return; + } + timerInfo.isRef = false; + core.unrefOp(timerInfo.promiseId); +} + +export { + clearInterval, + clearTimeout, + handleTimerMacrotask, + opNow, + refTimer, + setInterval, + setTimeout, + unrefTimer, +}; diff --git a/ext/web/03_abort_signal.js b/ext/web/03_abort_signal.js index cce1bac7e..96757f41f 100644 --- a/ext/web/03_abort_signal.js +++ b/ext/web/03_abort_signal.js @@ -1,200 +1,205 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; // @ts-check /// <reference path="../../core/internal.d.ts" /> -((window) => { - const webidl = window.__bootstrap.webidl; - const { Event, setIsTrusted, defineEventHandler } = window.__bootstrap.event; - const { EventTarget, listenerCount } = window.__bootstrap.eventTarget; - const { - SafeArrayIterator, - SafeSetIterator, - Set, - SetPrototypeAdd, - SetPrototypeDelete, - Symbol, - TypeError, - } = window.__bootstrap.primordials; - const { setTimeout, refTimer, unrefTimer } = window.__bootstrap.timers; - - const add = Symbol("[[add]]"); - const signalAbort = Symbol("[[signalAbort]]"); - const remove = Symbol("[[remove]]"); - const abortReason = Symbol("[[abortReason]]"); - const abortAlgos = Symbol("[[abortAlgos]]"); - const signal = Symbol("[[signal]]"); - const timerId = Symbol("[[timerId]]"); - - const illegalConstructorKey = Symbol("illegalConstructorKey"); - - class AbortSignal extends EventTarget { - static abort(reason = undefined) { - if (reason !== undefined) { - reason = webidl.converters.any(reason); - } - const signal = new AbortSignal(illegalConstructorKey); - signal[signalAbort](reason); - return signal; - } +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { + defineEventHandler, + Event, + EventTarget, + listenerCount, + setIsTrusted, +} from "internal:ext/web/02_event.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + SafeArrayIterator, + SafeSetIterator, + Set, + SetPrototypeAdd, + SetPrototypeDelete, + Symbol, + TypeError, +} = primordials; +import { + refTimer, + setTimeout, + unrefTimer, +} from "internal:ext/web/02_timers.js"; + +const add = Symbol("[[add]]"); +const signalAbort = Symbol("[[signalAbort]]"); +const remove = Symbol("[[remove]]"); +const abortReason = Symbol("[[abortReason]]"); +const abortAlgos = Symbol("[[abortAlgos]]"); +const signal = Symbol("[[signal]]"); +const timerId = Symbol("[[timerId]]"); + +const illegalConstructorKey = Symbol("illegalConstructorKey"); + +class AbortSignal extends EventTarget { + static abort(reason = undefined) { + if (reason !== undefined) { + reason = webidl.converters.any(reason); + } + const signal = new AbortSignal(illegalConstructorKey); + signal[signalAbort](reason); + return signal; + } - static timeout(millis) { - const prefix = "Failed to call 'AbortSignal.timeout'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - millis = webidl.converters["unsigned long long"](millis, { - enforceRange: true, - }); - - const signal = new AbortSignal(illegalConstructorKey); - signal[timerId] = setTimeout( - () => { - signal[timerId] = null; - signal[signalAbort]( - new DOMException("Signal timed out.", "TimeoutError"), - ); - }, - millis, - ); - unrefTimer(signal[timerId]); - return signal; - } + static timeout(millis) { + const prefix = "Failed to call 'AbortSignal.timeout'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + millis = webidl.converters["unsigned long long"](millis, { + enforceRange: true, + }); + + const signal = new AbortSignal(illegalConstructorKey); + signal[timerId] = setTimeout( + () => { + signal[timerId] = null; + signal[signalAbort]( + new DOMException("Signal timed out.", "TimeoutError"), + ); + }, + millis, + ); + unrefTimer(signal[timerId]); + return signal; + } - [add](algorithm) { - if (this.aborted) { - return; - } - if (this[abortAlgos] === null) { - this[abortAlgos] = new Set(); - } - SetPrototypeAdd(this[abortAlgos], algorithm); + [add](algorithm) { + if (this.aborted) { + return; } - - [signalAbort]( - reason = new DOMException("The signal has been aborted", "AbortError"), - ) { - if (this.aborted) { - return; - } - this[abortReason] = reason; - if (this[abortAlgos] !== null) { - for (const algorithm of new SafeSetIterator(this[abortAlgos])) { - algorithm(); - } - this[abortAlgos] = null; - } - const event = new Event("abort"); - setIsTrusted(event, true); - this.dispatchEvent(event); + if (this[abortAlgos] === null) { + this[abortAlgos] = new Set(); } + SetPrototypeAdd(this[abortAlgos], algorithm); + } - [remove](algorithm) { - this[abortAlgos] && SetPrototypeDelete(this[abortAlgos], algorithm); + [signalAbort]( + reason = new DOMException("The signal has been aborted", "AbortError"), + ) { + if (this.aborted) { + return; } - - constructor(key = null) { - if (key != illegalConstructorKey) { - throw new TypeError("Illegal constructor."); + this[abortReason] = reason; + if (this[abortAlgos] !== null) { + for (const algorithm of new SafeSetIterator(this[abortAlgos])) { + algorithm(); } - super(); - this[abortReason] = undefined; this[abortAlgos] = null; - this[timerId] = null; - this[webidl.brand] = webidl.brand; } + const event = new Event("abort"); + setIsTrusted(event, true); + this.dispatchEvent(event); + } - get aborted() { - webidl.assertBranded(this, AbortSignalPrototype); - return this[abortReason] !== undefined; - } + [remove](algorithm) { + this[abortAlgos] && SetPrototypeDelete(this[abortAlgos], algorithm); + } - get reason() { - webidl.assertBranded(this, AbortSignalPrototype); - return this[abortReason]; + constructor(key = null) { + if (key != illegalConstructorKey) { + throw new TypeError("Illegal constructor."); } + super(); + this[abortReason] = undefined; + this[abortAlgos] = null; + this[timerId] = null; + this[webidl.brand] = webidl.brand; + } - throwIfAborted() { - webidl.assertBranded(this, AbortSignalPrototype); - if (this[abortReason] !== undefined) { - throw this[abortReason]; - } + get aborted() { + webidl.assertBranded(this, AbortSignalPrototype); + return this[abortReason] !== undefined; + } + + get reason() { + webidl.assertBranded(this, AbortSignalPrototype); + return this[abortReason]; + } + + throwIfAborted() { + webidl.assertBranded(this, AbortSignalPrototype); + if (this[abortReason] !== undefined) { + throw this[abortReason]; } + } - // `addEventListener` and `removeEventListener` have to be overriden in - // order to have the timer block the event loop while there are listeners. - // `[add]` and `[remove]` don't ref and unref the timer because they can - // only be used by Deno internals, which use it to essentially cancel async - // ops which would block the event loop. - addEventListener(...args) { - super.addEventListener(...new SafeArrayIterator(args)); - if (this[timerId] !== null && listenerCount(this, "abort") > 0) { - refTimer(this[timerId]); - } + // `addEventListener` and `removeEventListener` have to be overriden in + // order to have the timer block the event loop while there are listeners. + // `[add]` and `[remove]` don't ref and unref the timer because they can + // only be used by Deno internals, which use it to essentially cancel async + // ops which would block the event loop. + addEventListener(...args) { + super.addEventListener(...new SafeArrayIterator(args)); + if (this[timerId] !== null && listenerCount(this, "abort") > 0) { + refTimer(this[timerId]); } + } - removeEventListener(...args) { - super.removeEventListener(...new SafeArrayIterator(args)); - if (this[timerId] !== null && listenerCount(this, "abort") === 0) { - unrefTimer(this[timerId]); - } + removeEventListener(...args) { + super.removeEventListener(...new SafeArrayIterator(args)); + if (this[timerId] !== null && listenerCount(this, "abort") === 0) { + unrefTimer(this[timerId]); } } - defineEventHandler(AbortSignal.prototype, "abort"); +} +defineEventHandler(AbortSignal.prototype, "abort"); - webidl.configurePrototype(AbortSignal); - const AbortSignalPrototype = AbortSignal.prototype; +webidl.configurePrototype(AbortSignal); +const AbortSignalPrototype = AbortSignal.prototype; - class AbortController { - [signal] = new AbortSignal(illegalConstructorKey); +class AbortController { + [signal] = new AbortSignal(illegalConstructorKey); - constructor() { - this[webidl.brand] = webidl.brand; - } + constructor() { + this[webidl.brand] = webidl.brand; + } - get signal() { - webidl.assertBranded(this, AbortControllerPrototype); - return this[signal]; - } + get signal() { + webidl.assertBranded(this, AbortControllerPrototype); + return this[signal]; + } - abort(reason) { - webidl.assertBranded(this, AbortControllerPrototype); - this[signal][signalAbort](reason); - } + abort(reason) { + webidl.assertBranded(this, AbortControllerPrototype); + this[signal][signalAbort](reason); } +} - webidl.configurePrototype(AbortController); - const AbortControllerPrototype = AbortController.prototype; +webidl.configurePrototype(AbortController); +const AbortControllerPrototype = AbortController.prototype; - webidl.converters["AbortSignal"] = webidl.createInterfaceConverter( - "AbortSignal", - AbortSignal.prototype, - ); +webidl.converters["AbortSignal"] = webidl.createInterfaceConverter( + "AbortSignal", + AbortSignal.prototype, +); - function newSignal() { - return new AbortSignal(illegalConstructorKey); - } +function newSignal() { + return new AbortSignal(illegalConstructorKey); +} - function follow(followingSignal, parentSignal) { - if (followingSignal.aborted) { - return; - } - if (parentSignal.aborted) { - followingSignal[signalAbort](parentSignal.reason); - } else { - parentSignal[add](() => - followingSignal[signalAbort](parentSignal.reason) - ); - } +function follow(followingSignal, parentSignal) { + if (followingSignal.aborted) { + return; } - - window.__bootstrap.abortSignal = { - AbortSignal, - AbortController, - AbortSignalPrototype, - add, - signalAbort, - remove, - follow, - newSignal, - }; -})(this); + if (parentSignal.aborted) { + followingSignal[signalAbort](parentSignal.reason); + } else { + parentSignal[add](() => followingSignal[signalAbort](parentSignal.reason)); + } +} + +export { + AbortController, + AbortSignal, + AbortSignalPrototype, + add, + follow, + newSignal, + remove, + signalAbort, +}; diff --git a/ext/web/04_global_interfaces.js b/ext/web/04_global_interfaces.js index 840f93ba9..6a42968db 100644 --- a/ext/web/04_global_interfaces.js +++ b/ext/web/04_global_interfaces.js @@ -1,79 +1,83 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; // @ts-check /// <reference path="../../core/internal.d.ts" /> -((window) => { - const { EventTarget } = window.__bootstrap.eventTarget; - const { - Symbol, - SymbolToStringTag, - TypeError, - } = window.__bootstrap.primordials; +import { EventTarget } from "internal:ext/web/02_event.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + Symbol, + SymbolToStringTag, + TypeError, +} = primordials; - const illegalConstructorKey = Symbol("illegalConstructorKey"); +const illegalConstructorKey = Symbol("illegalConstructorKey"); - class Window extends EventTarget { - constructor(key = null) { - if (key !== illegalConstructorKey) { - throw new TypeError("Illegal constructor."); - } - super(); +class Window extends EventTarget { + constructor(key = null) { + if (key !== illegalConstructorKey) { + throw new TypeError("Illegal constructor."); } + super(); + } - get [SymbolToStringTag]() { - return "Window"; - } + get [SymbolToStringTag]() { + return "Window"; } +} - class WorkerGlobalScope extends EventTarget { - constructor(key = null) { - if (key != illegalConstructorKey) { - throw new TypeError("Illegal constructor."); - } - super(); +class WorkerGlobalScope extends EventTarget { + constructor(key = null) { + if (key != illegalConstructorKey) { + throw new TypeError("Illegal constructor."); } + super(); + } - get [SymbolToStringTag]() { - return "WorkerGlobalScope"; - } + get [SymbolToStringTag]() { + return "WorkerGlobalScope"; } +} - class DedicatedWorkerGlobalScope extends WorkerGlobalScope { - constructor(key = null) { - if (key != illegalConstructorKey) { - throw new TypeError("Illegal constructor."); - } - super(); +class DedicatedWorkerGlobalScope extends WorkerGlobalScope { + constructor(key = null) { + if (key != illegalConstructorKey) { + throw new TypeError("Illegal constructor."); } + super(); + } - get [SymbolToStringTag]() { - return "DedicatedWorkerGlobalScope"; - } + get [SymbolToStringTag]() { + return "DedicatedWorkerGlobalScope"; } +} + +const dedicatedWorkerGlobalScopeConstructorDescriptor = { + configurable: true, + enumerable: false, + value: DedicatedWorkerGlobalScope, + writable: true, +}; + +const windowConstructorDescriptor = { + configurable: true, + enumerable: false, + value: Window, + writable: true, +}; + +const workerGlobalScopeConstructorDescriptor = { + configurable: true, + enumerable: false, + value: WorkerGlobalScope, + writable: true, +}; - window.__bootstrap.globalInterfaces = { - DedicatedWorkerGlobalScope, - Window, - WorkerGlobalScope, - dedicatedWorkerGlobalScopeConstructorDescriptor: { - configurable: true, - enumerable: false, - value: DedicatedWorkerGlobalScope, - writable: true, - }, - windowConstructorDescriptor: { - configurable: true, - enumerable: false, - value: Window, - writable: true, - }, - workerGlobalScopeConstructorDescriptor: { - configurable: true, - enumerable: false, - value: WorkerGlobalScope, - writable: true, - }, - }; -})(this); +export { + DedicatedWorkerGlobalScope, + dedicatedWorkerGlobalScopeConstructorDescriptor, + Window, + windowConstructorDescriptor, + WorkerGlobalScope, + workerGlobalScopeConstructorDescriptor, +}; diff --git a/ext/web/05_base64.js b/ext/web/05_base64.js index dac366ca0..9f11ec97c 100644 --- a/ext/web/05_base64.js +++ b/ext/web/05_base64.js @@ -6,68 +6,62 @@ /// <reference path="../web/internal.d.ts" /> /// <reference lib="esnext" /> -"use strict"; +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import DOMException from "internal:ext/web/01_dom_exception.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ObjectPrototypeIsPrototypeOf, + TypeErrorPrototype, +} = primordials; -((window) => { - const core = Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { DOMException } = window.__bootstrap.domException; - const { - ObjectPrototypeIsPrototypeOf, - TypeErrorPrototype, - } = window.__bootstrap.primordials; - - /** - * @param {string} data - * @returns {string} - */ - function atob(data) { - const prefix = "Failed to execute 'atob'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - data = webidl.converters.DOMString(data, { - prefix, - context: "Argument 1", - }); - try { - return ops.op_base64_atob(data); - } catch (e) { - if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) { - throw new DOMException( - "Failed to decode base64: invalid character", - "InvalidCharacterError", - ); - } - throw e; +/** + * @param {string} data + * @returns {string} + */ +function atob(data) { + const prefix = "Failed to execute 'atob'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + data = webidl.converters.DOMString(data, { + prefix, + context: "Argument 1", + }); + try { + return ops.op_base64_atob(data); + } catch (e) { + if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) { + throw new DOMException( + "Failed to decode base64: invalid character", + "InvalidCharacterError", + ); } + throw e; } +} - /** - * @param {string} data - * @returns {string} - */ - function btoa(data) { - const prefix = "Failed to execute 'btoa'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - data = webidl.converters.DOMString(data, { - prefix, - context: "Argument 1", - }); - try { - return ops.op_base64_btoa(data); - } catch (e) { - if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) { - throw new DOMException( - "The string to be encoded contains characters outside of the Latin1 range.", - "InvalidCharacterError", - ); - } - throw e; +/** + * @param {string} data + * @returns {string} + */ +function btoa(data) { + const prefix = "Failed to execute 'btoa'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + data = webidl.converters.DOMString(data, { + prefix, + context: "Argument 1", + }); + try { + return ops.op_base64_btoa(data); + } catch (e) { + if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) { + throw new DOMException( + "The string to be encoded contains characters outside of the Latin1 range.", + "InvalidCharacterError", + ); } + throw e; } +} - window.__bootstrap.base64 = { - atob, - btoa, - }; -})(globalThis); +export { atob, btoa }; diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js index 1bad4f314..a88b60893 100644 --- a/ext/web/06_streams.js +++ b/ext/web/06_streams.js @@ -5,3092 +5,3199 @@ /// <reference path="./06_streams_types.d.ts" /> /// <reference path="./lib.deno_web.d.ts" /> /// <reference lib="esnext" /> -"use strict"; - -((window) => { - const core = window.Deno.core; - const webidl = window.__bootstrap.webidl; - const { add, remove, signalAbort, newSignal, AbortSignalPrototype } = - window.__bootstrap.abortSignal; - const { - ArrayBuffer, - ArrayBufferPrototype, - ArrayBufferIsView, - ArrayPrototypeMap, - ArrayPrototypePush, - ArrayPrototypeShift, - AsyncGeneratorPrototype, - BigInt64ArrayPrototype, - BigUint64ArrayPrototype, - DataView, - FinalizationRegistry, - Int8ArrayPrototype, - Int16ArrayPrototype, - Int32ArrayPrototype, - NumberIsInteger, - NumberIsNaN, - MathMin, - ObjectCreate, - ObjectDefineProperties, - ObjectDefineProperty, - ObjectGetPrototypeOf, - ObjectPrototype, - ObjectPrototypeIsPrototypeOf, - ObjectSetPrototypeOf, - Promise, - PromisePrototypeCatch, - PromisePrototypeThen, - PromiseReject, - PromiseResolve, - queueMicrotask, - RangeError, - ReflectHas, - SafePromiseAll, - // TODO(lucacasonato): add SharedArrayBuffer to primordials - // SharedArrayBufferPrototype - Symbol, - SymbolAsyncIterator, - SymbolFor, - TypeError, - TypedArrayPrototypeSet, - Uint8Array, - Uint8ArrayPrototype, - Uint16ArrayPrototype, - Uint32ArrayPrototype, - Uint8ClampedArrayPrototype, - WeakMap, - WeakMapPrototypeGet, - WeakMapPrototypeHas, - WeakMapPrototypeSet, - } = globalThis.__bootstrap.primordials; - const consoleInternal = window.__bootstrap.console; - const ops = core.ops; - const { AssertionError, assert } = window.__bootstrap.infra; - - /** @template T */ - class Deferred { - /** @type {Promise<T>} */ - #promise; - /** @type {(reject?: any) => void} */ - #reject; - /** @type {(value: T | PromiseLike<T>) => void} */ - #resolve; - /** @type {"pending" | "fulfilled"} */ - #state = "pending"; - - constructor() { - this.#promise = new Promise((resolve, reject) => { - this.#resolve = resolve; - this.#reject = reject; - }); - } - - /** @returns {Promise<T>} */ - get promise() { - return this.#promise; - } - - /** @returns {"pending" | "fulfilled"} */ - get state() { - return this.#state; - } - - /** @param {any=} reason */ - reject(reason) { - // already settled promises are a no-op - if (this.#state !== "pending") { - return; - } - this.#state = "fulfilled"; - this.#reject(reason); - } - - /** @param {T | PromiseLike<T>} value */ - resolve(value) { - // already settled promises are a no-op - if (this.#state !== "pending") { - return; - } - this.#state = "fulfilled"; - this.#resolve(value); - } - } - - /** - * @template T - * @param {T | PromiseLike<T>} value - * @returns {Promise<T>} - */ - function resolvePromiseWith(value) { - return new Promise((resolve) => resolve(value)); - } - - /** @param {any} e */ - function rethrowAssertionErrorRejection(e) { - if (e && ObjectPrototypeIsPrototypeOf(AssertionError.prototype, e)) { - queueMicrotask(() => { - console.error(`Internal Error: ${e.stack}`); - }); - } - } - - /** @param {Promise<any>} promise */ - function setPromiseIsHandledToTrue(promise) { - PromisePrototypeThen(promise, undefined, rethrowAssertionErrorRejection); - } - - /** - * @template T - * @template TResult1 - * @template TResult2 - * @param {Promise<T>} promise - * @param {(value: T) => TResult1 | PromiseLike<TResult1>} fulfillmentHandler - * @param {(reason: any) => TResult2 | PromiseLike<TResult2>=} rejectionHandler - * @returns {Promise<TResult1 | TResult2>} - */ - function transformPromiseWith(promise, fulfillmentHandler, rejectionHandler) { - return PromisePrototypeThen(promise, fulfillmentHandler, rejectionHandler); - } - - /** - * @template T - * @template TResult - * @param {Promise<T>} promise - * @param {(value: T) => TResult | PromiseLike<TResult>} onFulfilled - * @returns {void} - */ - function uponFulfillment(promise, onFulfilled) { - uponPromise(promise, onFulfilled); - } - /** - * @template T - * @template TResult - * @param {Promise<T>} promise - * @param {(value: T) => TResult | PromiseLike<TResult>} onRejected - * @returns {void} - */ - function uponRejection(promise, onRejected) { - uponPromise(promise, undefined, onRejected); +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { + AbortSignalPrototype, + add, + newSignal, + remove, + signalAbort, +} from "internal:ext/web/03_abort_signal.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayBuffer, + ArrayBufferPrototype, + ArrayBufferIsView, + ArrayPrototypeMap, + ArrayPrototypePush, + ArrayPrototypeShift, + AsyncGeneratorPrototype, + BigInt64ArrayPrototype, + BigUint64ArrayPrototype, + DataView, + FinalizationRegistry, + Int8ArrayPrototype, + Int16ArrayPrototype, + Int32ArrayPrototype, + NumberIsInteger, + NumberIsNaN, + MathMin, + ObjectCreate, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectGetPrototypeOf, + ObjectPrototype, + ObjectPrototypeIsPrototypeOf, + ObjectSetPrototypeOf, + Promise, + PromisePrototypeCatch, + PromisePrototypeThen, + PromiseReject, + PromiseResolve, + queueMicrotask, + RangeError, + ReflectHas, + SafePromiseAll, + // TODO(lucacasonato): add SharedArrayBuffer to primordials + // SharedArrayBufferPrototype + Symbol, + SymbolAsyncIterator, + SymbolFor, + TypeError, + TypedArrayPrototypeSet, + Uint8Array, + Uint8ArrayPrototype, + Uint16ArrayPrototype, + Uint32ArrayPrototype, + Uint8ClampedArrayPrototype, + WeakMap, + WeakMapPrototypeGet, + WeakMapPrototypeHas, + WeakMapPrototypeSet, +} = primordials; +import { createFilteredInspectProxy } from "internal:ext/console/02_console.js"; +import { assert, AssertionError } from "internal:ext/web/00_infra.js"; + +/** @template T */ +class Deferred { + /** @type {Promise<T>} */ + #promise; + /** @type {(reject?: any) => void} */ + #reject; + /** @type {(value: T | PromiseLike<T>) => void} */ + #resolve; + /** @type {"pending" | "fulfilled"} */ + #state = "pending"; + + constructor() { + this.#promise = new Promise((resolve, reject) => { + this.#resolve = resolve; + this.#reject = reject; + }); } - /** - * @template T - * @template TResult1 - * @template TResult2 - * @param {Promise<T>} promise - * @param {(value: T) => TResult1 | PromiseLike<TResult1>} onFulfilled - * @param {(reason: any) => TResult2 | PromiseLike<TResult2>=} onRejected - * @returns {void} - */ - function uponPromise(promise, onFulfilled, onRejected) { - PromisePrototypeThen( - PromisePrototypeThen(promise, onFulfilled, onRejected), - undefined, - rethrowAssertionErrorRejection, - ); + /** @returns {Promise<T>} */ + get promise() { + return this.#promise; } - /** - * @param {ArrayBufferLike} O - * @returns {boolean} - */ - function isDetachedBuffer(O) { - return O.byteLength === 0 && ops.op_arraybuffer_was_detached(O); + /** @returns {"pending" | "fulfilled"} */ + get state() { + return this.#state; } - /** - * @param {ArrayBufferLike} O - * @returns {boolean} - */ - function canTransferArrayBuffer(O) { - assert(typeof O === "object"); - assert( - ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, O) || - // deno-lint-ignore prefer-primordials - ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, O), - ); - if (isDetachedBuffer(O)) { - return false; + /** @param {any=} reason */ + reject(reason) { + // already settled promises are a no-op + if (this.#state !== "pending") { + return; } - // TODO(@crowlKats): 4. If SameValue(O.[[ArrayBufferDetachKey]], undefined) is false, return false. - return true; - } - - /** - * @param {ArrayBufferLike} O - * @returns {ArrayBufferLike} - */ - function transferArrayBuffer(O) { - return ops.op_transfer_arraybuffer(O); - } - - /** - * @param {ArrayBufferView} O - * @returns {Uint8Array} - */ - function cloneAsUint8Array(O) { - assert(typeof O === "object"); - assert(ArrayBufferIsView(O)); - assert(!isDetachedBuffer(O.buffer)); - const buffer = O.buffer.slice(O.byteOffset, O.byteOffset + O.byteLength); - return new Uint8Array(buffer); - } - - const _abortAlgorithm = Symbol("[[abortAlgorithm]]"); - const _abortSteps = Symbol("[[AbortSteps]]"); - const _autoAllocateChunkSize = Symbol("[[autoAllocateChunkSize]]"); - const _backpressure = Symbol("[[backpressure]]"); - const _backpressureChangePromise = Symbol("[[backpressureChangePromise]]"); - const _byobRequest = Symbol("[[byobRequest]]"); - const _cancelAlgorithm = Symbol("[[cancelAlgorithm]]"); - const _cancelSteps = Symbol("[[CancelSteps]]"); - const _close = Symbol("close sentinel"); - const _closeAlgorithm = Symbol("[[closeAlgorithm]]"); - const _closedPromise = Symbol("[[closedPromise]]"); - const _closeRequest = Symbol("[[closeRequest]]"); - const _closeRequested = Symbol("[[closeRequested]]"); - const _controller = Symbol("[[controller]]"); - const _detached = Symbol("[[Detached]]"); - const _disturbed = Symbol("[[disturbed]]"); - const _errorSteps = Symbol("[[ErrorSteps]]"); - const _flushAlgorithm = Symbol("[[flushAlgorithm]]"); - const _globalObject = Symbol("[[globalObject]]"); - const _highWaterMark = Symbol("[[highWaterMark]]"); - const _inFlightCloseRequest = Symbol("[[inFlightCloseRequest]]"); - const _inFlightWriteRequest = Symbol("[[inFlightWriteRequest]]"); - const _pendingAbortRequest = Symbol("[pendingAbortRequest]"); - const _pendingPullIntos = Symbol("[[pendingPullIntos]]"); - const _preventCancel = Symbol("[[preventCancel]]"); - const _pullAgain = Symbol("[[pullAgain]]"); - const _pullAlgorithm = Symbol("[[pullAlgorithm]]"); - const _pulling = Symbol("[[pulling]]"); - const _pullSteps = Symbol("[[PullSteps]]"); - const _releaseSteps = Symbol("[[ReleaseSteps]]"); - const _queue = Symbol("[[queue]]"); - const _queueTotalSize = Symbol("[[queueTotalSize]]"); - const _readable = Symbol("[[readable]]"); - const _reader = Symbol("[[reader]]"); - const _readRequests = Symbol("[[readRequests]]"); - const _readIntoRequests = Symbol("[[readIntoRequests]]"); - const _readyPromise = Symbol("[[readyPromise]]"); - const _signal = Symbol("[[signal]]"); - const _started = Symbol("[[started]]"); - const _state = Symbol("[[state]]"); - const _storedError = Symbol("[[storedError]]"); - const _strategyHWM = Symbol("[[strategyHWM]]"); - const _strategySizeAlgorithm = Symbol("[[strategySizeAlgorithm]]"); - const _stream = Symbol("[[stream]]"); - const _transformAlgorithm = Symbol("[[transformAlgorithm]]"); - const _view = Symbol("[[view]]"); - const _writable = Symbol("[[writable]]"); - const _writeAlgorithm = Symbol("[[writeAlgorithm]]"); - const _writer = Symbol("[[writer]]"); - const _writeRequests = Symbol("[[writeRequests]]"); - - /** - * @template R - * @param {ReadableStream<R>} stream - * @returns {ReadableStreamDefaultReader<R>} - */ - function acquireReadableStreamDefaultReader(stream) { - return new ReadableStreamDefaultReader(stream); + this.#state = "fulfilled"; + this.#reject(reason); } - /** - * @template R - * @param {ReadableStream<R>} stream - * @returns {ReadableStreamBYOBReader<R>} - */ - function acquireReadableStreamBYOBReader(stream) { - const reader = webidl.createBranded(ReadableStreamBYOBReader); - setUpReadableStreamBYOBReader(reader, stream); - return reader; + /** @param {T | PromiseLike<T>} value */ + resolve(value) { + // already settled promises are a no-op + if (this.#state !== "pending") { + return; + } + this.#state = "fulfilled"; + this.#resolve(value); + } +} + +/** + * @template T + * @param {T | PromiseLike<T>} value + * @returns {Promise<T>} + */ +function resolvePromiseWith(value) { + return new Promise((resolve) => resolve(value)); +} + +/** @param {any} e */ +function rethrowAssertionErrorRejection(e) { + if (e && ObjectPrototypeIsPrototypeOf(AssertionError.prototype, e)) { + queueMicrotask(() => { + console.error(`Internal Error: ${e.stack}`); + }); } - - /** - * @template W - * @param {WritableStream<W>} stream - * @returns {WritableStreamDefaultWriter<W>} - */ - function acquireWritableStreamDefaultWriter(stream) { - return new WritableStreamDefaultWriter(stream); +} + +/** @param {Promise<any>} promise */ +function setPromiseIsHandledToTrue(promise) { + PromisePrototypeThen(promise, undefined, rethrowAssertionErrorRejection); +} + +/** + * @template T + * @template TResult1 + * @template TResult2 + * @param {Promise<T>} promise + * @param {(value: T) => TResult1 | PromiseLike<TResult1>} fulfillmentHandler + * @param {(reason: any) => TResult2 | PromiseLike<TResult2>=} rejectionHandler + * @returns {Promise<TResult1 | TResult2>} + */ +function transformPromiseWith(promise, fulfillmentHandler, rejectionHandler) { + return PromisePrototypeThen(promise, fulfillmentHandler, rejectionHandler); +} + +/** + * @template T + * @template TResult + * @param {Promise<T>} promise + * @param {(value: T) => TResult | PromiseLike<TResult>} onFulfilled + * @returns {void} + */ +function uponFulfillment(promise, onFulfilled) { + uponPromise(promise, onFulfilled); +} + +/** + * @template T + * @template TResult + * @param {Promise<T>} promise + * @param {(value: T) => TResult | PromiseLike<TResult>} onRejected + * @returns {void} + */ +function uponRejection(promise, onRejected) { + uponPromise(promise, undefined, onRejected); +} + +/** + * @template T + * @template TResult1 + * @template TResult2 + * @param {Promise<T>} promise + * @param {(value: T) => TResult1 | PromiseLike<TResult1>} onFulfilled + * @param {(reason: any) => TResult2 | PromiseLike<TResult2>=} onRejected + * @returns {void} + */ +function uponPromise(promise, onFulfilled, onRejected) { + PromisePrototypeThen( + PromisePrototypeThen(promise, onFulfilled, onRejected), + undefined, + rethrowAssertionErrorRejection, + ); +} + +/** + * @param {ArrayBufferLike} O + * @returns {boolean} + */ +function isDetachedBuffer(O) { + return O.byteLength === 0 && ops.op_arraybuffer_was_detached(O); +} + +/** + * @param {ArrayBufferLike} O + * @returns {boolean} + */ +function canTransferArrayBuffer(O) { + assert(typeof O === "object"); + assert( + ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, O) || + // deno-lint-ignore prefer-primordials + ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, O), + ); + if (isDetachedBuffer(O)) { + return false; } - - /** - * @template R - * @param {() => void} startAlgorithm - * @param {() => Promise<void>} pullAlgorithm - * @param {(reason: any) => Promise<void>} cancelAlgorithm - * @param {number=} highWaterMark - * @param {((chunk: R) => number)=} sizeAlgorithm - * @returns {ReadableStream<R>} - */ - function createReadableStream( + // TODO(@crowlKats): 4. If SameValue(O.[[ArrayBufferDetachKey]], undefined) is false, return false. + return true; +} + +/** + * @param {ArrayBufferLike} O + * @returns {ArrayBufferLike} + */ +function transferArrayBuffer(O) { + return ops.op_transfer_arraybuffer(O); +} + +/** + * @param {ArrayBufferView} O + * @returns {Uint8Array} + */ +function cloneAsUint8Array(O) { + assert(typeof O === "object"); + assert(ArrayBufferIsView(O)); + assert(!isDetachedBuffer(O.buffer)); + const buffer = O.buffer.slice(O.byteOffset, O.byteOffset + O.byteLength); + return new Uint8Array(buffer); +} + +const _abortAlgorithm = Symbol("[[abortAlgorithm]]"); +const _abortSteps = Symbol("[[AbortSteps]]"); +const _autoAllocateChunkSize = Symbol("[[autoAllocateChunkSize]]"); +const _backpressure = Symbol("[[backpressure]]"); +const _backpressureChangePromise = Symbol("[[backpressureChangePromise]]"); +const _byobRequest = Symbol("[[byobRequest]]"); +const _cancelAlgorithm = Symbol("[[cancelAlgorithm]]"); +const _cancelSteps = Symbol("[[CancelSteps]]"); +const _close = Symbol("close sentinel"); +const _closeAlgorithm = Symbol("[[closeAlgorithm]]"); +const _closedPromise = Symbol("[[closedPromise]]"); +const _closeRequest = Symbol("[[closeRequest]]"); +const _closeRequested = Symbol("[[closeRequested]]"); +const _controller = Symbol("[[controller]]"); +const _detached = Symbol("[[Detached]]"); +const _disturbed = Symbol("[[disturbed]]"); +const _errorSteps = Symbol("[[ErrorSteps]]"); +const _flushAlgorithm = Symbol("[[flushAlgorithm]]"); +const _globalObject = Symbol("[[globalObject]]"); +const _highWaterMark = Symbol("[[highWaterMark]]"); +const _inFlightCloseRequest = Symbol("[[inFlightCloseRequest]]"); +const _inFlightWriteRequest = Symbol("[[inFlightWriteRequest]]"); +const _pendingAbortRequest = Symbol("[pendingAbortRequest]"); +const _pendingPullIntos = Symbol("[[pendingPullIntos]]"); +const _preventCancel = Symbol("[[preventCancel]]"); +const _pullAgain = Symbol("[[pullAgain]]"); +const _pullAlgorithm = Symbol("[[pullAlgorithm]]"); +const _pulling = Symbol("[[pulling]]"); +const _pullSteps = Symbol("[[PullSteps]]"); +const _releaseSteps = Symbol("[[ReleaseSteps]]"); +const _queue = Symbol("[[queue]]"); +const _queueTotalSize = Symbol("[[queueTotalSize]]"); +const _readable = Symbol("[[readable]]"); +const _reader = Symbol("[[reader]]"); +const _readRequests = Symbol("[[readRequests]]"); +const _readIntoRequests = Symbol("[[readIntoRequests]]"); +const _readyPromise = Symbol("[[readyPromise]]"); +const _signal = Symbol("[[signal]]"); +const _started = Symbol("[[started]]"); +const _state = Symbol("[[state]]"); +const _storedError = Symbol("[[storedError]]"); +const _strategyHWM = Symbol("[[strategyHWM]]"); +const _strategySizeAlgorithm = Symbol("[[strategySizeAlgorithm]]"); +const _stream = Symbol("[[stream]]"); +const _transformAlgorithm = Symbol("[[transformAlgorithm]]"); +const _view = Symbol("[[view]]"); +const _writable = Symbol("[[writable]]"); +const _writeAlgorithm = Symbol("[[writeAlgorithm]]"); +const _writer = Symbol("[[writer]]"); +const _writeRequests = Symbol("[[writeRequests]]"); + +/** + * @template R + * @param {ReadableStream<R>} stream + * @returns {ReadableStreamDefaultReader<R>} + */ +function acquireReadableStreamDefaultReader(stream) { + return new ReadableStreamDefaultReader(stream); +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @returns {ReadableStreamBYOBReader<R>} + */ +function acquireReadableStreamBYOBReader(stream) { + const reader = webidl.createBranded(ReadableStreamBYOBReader); + setUpReadableStreamBYOBReader(reader, stream); + return reader; +} + +/** + * @template W + * @param {WritableStream<W>} stream + * @returns {WritableStreamDefaultWriter<W>} + */ +function acquireWritableStreamDefaultWriter(stream) { + return new WritableStreamDefaultWriter(stream); +} + +/** + * @template R + * @param {() => void} startAlgorithm + * @param {() => Promise<void>} pullAlgorithm + * @param {(reason: any) => Promise<void>} cancelAlgorithm + * @param {number=} highWaterMark + * @param {((chunk: R) => number)=} sizeAlgorithm + * @returns {ReadableStream<R>} + */ +function createReadableStream( + startAlgorithm, + pullAlgorithm, + cancelAlgorithm, + highWaterMark = 1, + sizeAlgorithm = () => 1, +) { + assert(isNonNegativeNumber(highWaterMark)); + /** @type {ReadableStream} */ + const stream = webidl.createBranded(ReadableStream); + initializeReadableStream(stream); + const controller = webidl.createBranded(ReadableStreamDefaultController); + setUpReadableStreamDefaultController( + stream, + controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, - highWaterMark = 1, - sizeAlgorithm = () => 1, - ) { - assert(isNonNegativeNumber(highWaterMark)); - /** @type {ReadableStream} */ - const stream = webidl.createBranded(ReadableStream); - initializeReadableStream(stream); - const controller = webidl.createBranded(ReadableStreamDefaultController); - setUpReadableStreamDefaultController( - stream, - controller, - startAlgorithm, - pullAlgorithm, - cancelAlgorithm, - highWaterMark, - sizeAlgorithm, - ); - return stream; - } - - /** - * @template W - * @param {(controller: WritableStreamDefaultController<W>) => Promise<void>} startAlgorithm - * @param {(chunk: W) => Promise<void>} writeAlgorithm - * @param {() => Promise<void>} closeAlgorithm - * @param {(reason: any) => Promise<void>} abortAlgorithm - * @param {number} highWaterMark - * @param {(chunk: W) => number} sizeAlgorithm - * @returns {WritableStream<W>} - */ - function createWritableStream( + highWaterMark, + sizeAlgorithm, + ); + return stream; +} + +/** + * @template W + * @param {(controller: WritableStreamDefaultController<W>) => Promise<void>} startAlgorithm + * @param {(chunk: W) => Promise<void>} writeAlgorithm + * @param {() => Promise<void>} closeAlgorithm + * @param {(reason: any) => Promise<void>} abortAlgorithm + * @param {number} highWaterMark + * @param {(chunk: W) => number} sizeAlgorithm + * @returns {WritableStream<W>} + */ +function createWritableStream( + startAlgorithm, + writeAlgorithm, + closeAlgorithm, + abortAlgorithm, + highWaterMark, + sizeAlgorithm, +) { + assert(isNonNegativeNumber(highWaterMark)); + const stream = webidl.createBranded(WritableStream); + initializeWritableStream(stream); + const controller = webidl.createBranded(WritableStreamDefaultController); + setUpWritableStreamDefaultController( + stream, + controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm, - ) { - assert(isNonNegativeNumber(highWaterMark)); - const stream = webidl.createBranded(WritableStream); - initializeWritableStream(stream); - const controller = webidl.createBranded(WritableStreamDefaultController); - setUpWritableStreamDefaultController( - stream, - controller, - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - highWaterMark, - sizeAlgorithm, - ); - return stream; + ); + return stream; +} + +/** + * @template T + * @param {{ [_queue]: Array<ValueWithSize<T>>, [_queueTotalSize]: number }} container + * @returns {T} + */ +function dequeueValue(container) { + assert( + ReflectHas(container, _queue) && + ReflectHas(container, _queueTotalSize), + ); + assert(container[_queue].length); + const valueWithSize = ArrayPrototypeShift(container[_queue]); + container[_queueTotalSize] -= valueWithSize.size; + if (container[_queueTotalSize] < 0) { + container[_queueTotalSize] = 0; } - - /** - * @template T - * @param {{ [_queue]: Array<ValueWithSize<T>>, [_queueTotalSize]: number }} container - * @returns {T} - */ - function dequeueValue(container) { - assert( - ReflectHas(container, _queue) && - ReflectHas(container, _queueTotalSize), + return valueWithSize.value; +} + +/** + * @template T + * @param {{ [_queue]: Array<ValueWithSize<T | _close>>, [_queueTotalSize]: number }} container + * @param {T} value + * @param {number} size + * @returns {void} + */ +function enqueueValueWithSize(container, value, size) { + assert( + ReflectHas(container, _queue) && + ReflectHas(container, _queueTotalSize), + ); + if (isNonNegativeNumber(size) === false) { + throw RangeError("chunk size isn't a positive number"); + } + if (size === Infinity) { + throw RangeError("chunk size is invalid"); + } + ArrayPrototypePush(container[_queue], { value, size }); + container[_queueTotalSize] += size; +} + +/** + * @param {QueuingStrategy} strategy + * @param {number} defaultHWM + */ +function extractHighWaterMark(strategy, defaultHWM) { + if (strategy.highWaterMark === undefined) { + return defaultHWM; + } + const highWaterMark = strategy.highWaterMark; + if (NumberIsNaN(highWaterMark) || highWaterMark < 0) { + throw RangeError( + `Expected highWaterMark to be a positive number or Infinity, got "${highWaterMark}".`, ); - assert(container[_queue].length); - const valueWithSize = ArrayPrototypeShift(container[_queue]); - container[_queueTotalSize] -= valueWithSize.size; - if (container[_queueTotalSize] < 0) { - container[_queueTotalSize] = 0; - } - return valueWithSize.value; } - - /** - * @template T - * @param {{ [_queue]: Array<ValueWithSize<T | _close>>, [_queueTotalSize]: number }} container - * @param {T} value - * @param {number} size - * @returns {void} - */ - function enqueueValueWithSize(container, value, size) { - assert( - ReflectHas(container, _queue) && - ReflectHas(container, _queueTotalSize), + return highWaterMark; +} + +/** + * @template T + * @param {QueuingStrategy<T>} strategy + * @return {(chunk: T) => number} + */ +function extractSizeAlgorithm(strategy) { + if (strategy.size === undefined) { + return () => 1; + } + return (chunk) => + webidl.invokeCallbackFunction( + strategy.size, + [chunk], + undefined, + webidl.converters["unrestricted double"], + { prefix: "Failed to call `sizeAlgorithm`" }, ); - if (isNonNegativeNumber(size) === false) { - throw RangeError("chunk size isn't a positive number"); - } - if (size === Infinity) { - throw RangeError("chunk size is invalid"); - } - ArrayPrototypePush(container[_queue], { value, size }); - container[_queueTotalSize] += size; - } - - /** - * @param {QueuingStrategy} strategy - * @param {number} defaultHWM - */ - function extractHighWaterMark(strategy, defaultHWM) { - if (strategy.highWaterMark === undefined) { - return defaultHWM; - } - const highWaterMark = strategy.highWaterMark; - if (NumberIsNaN(highWaterMark) || highWaterMark < 0) { - throw RangeError( - `Expected highWaterMark to be a positive number or Infinity, got "${highWaterMark}".`, - ); - } - return highWaterMark; - } - - /** - * @template T - * @param {QueuingStrategy<T>} strategy - * @return {(chunk: T) => number} - */ - function extractSizeAlgorithm(strategy) { - if (strategy.size === undefined) { - return () => 1; - } - return (chunk) => - webidl.invokeCallbackFunction( - strategy.size, - [chunk], - undefined, - webidl.converters["unrestricted double"], - { prefix: "Failed to call `sizeAlgorithm`" }, - ); - } - - /** - * @param {() => void} startAlgorithm - * @param {() => Promise<void>} pullAlgorithm - * @param {(reason: any) => Promise<void>} cancelAlgorithm - * @returns {ReadableStream} - */ - function createReadableByteStream( +} + +/** + * @param {() => void} startAlgorithm + * @param {() => Promise<void>} pullAlgorithm + * @param {(reason: any) => Promise<void>} cancelAlgorithm + * @returns {ReadableStream} + */ +function createReadableByteStream( + startAlgorithm, + pullAlgorithm, + cancelAlgorithm, +) { + const stream = webidl.createBranded(ReadableStream); + initializeReadableStream(stream); + const controller = webidl.createBranded(ReadableByteStreamController); + setUpReadableByteStreamController( + stream, + controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, - ) { - const stream = webidl.createBranded(ReadableStream); - initializeReadableStream(stream); - const controller = webidl.createBranded(ReadableByteStreamController); - setUpReadableByteStreamController( - stream, - controller, - startAlgorithm, - pullAlgorithm, - cancelAlgorithm, - 0, - undefined, - ); - return stream; - } - - /** - * @param {ReadableStream} stream - * @returns {void} - */ - function initializeReadableStream(stream) { - stream[_state] = "readable"; - stream[_reader] = stream[_storedError] = undefined; - stream[_disturbed] = false; - } - - /** - * @template I - * @template O - * @param {TransformStream<I, O>} stream - * @param {Deferred<void>} startPromise - * @param {number} writableHighWaterMark - * @param {(chunk: I) => number} writableSizeAlgorithm - * @param {number} readableHighWaterMark - * @param {(chunk: O) => number} readableSizeAlgorithm - */ - function initializeTransformStream( - stream, - startPromise, + 0, + undefined, + ); + return stream; +} + +/** + * @param {ReadableStream} stream + * @returns {void} + */ +function initializeReadableStream(stream) { + stream[_state] = "readable"; + stream[_reader] = stream[_storedError] = undefined; + stream[_disturbed] = false; +} + +/** + * @template I + * @template O + * @param {TransformStream<I, O>} stream + * @param {Deferred<void>} startPromise + * @param {number} writableHighWaterMark + * @param {(chunk: I) => number} writableSizeAlgorithm + * @param {number} readableHighWaterMark + * @param {(chunk: O) => number} readableSizeAlgorithm + */ +function initializeTransformStream( + stream, + startPromise, + writableHighWaterMark, + writableSizeAlgorithm, + readableHighWaterMark, + readableSizeAlgorithm, +) { + function startAlgorithm() { + return startPromise.promise; + } + + function writeAlgorithm(chunk) { + return transformStreamDefaultSinkWriteAlgorithm(stream, chunk); + } + + function abortAlgorithm(reason) { + return transformStreamDefaultSinkAbortAlgorithm(stream, reason); + } + + function closeAlgorithm() { + return transformStreamDefaultSinkCloseAlgorithm(stream); + } + + stream[_writable] = createWritableStream( + startAlgorithm, + writeAlgorithm, + closeAlgorithm, + abortAlgorithm, writableHighWaterMark, writableSizeAlgorithm, - readableHighWaterMark, - readableSizeAlgorithm, - ) { - function startAlgorithm() { - return startPromise.promise; - } - - function writeAlgorithm(chunk) { - return transformStreamDefaultSinkWriteAlgorithm(stream, chunk); - } - - function abortAlgorithm(reason) { - return transformStreamDefaultSinkAbortAlgorithm(stream, reason); - } - - function closeAlgorithm() { - return transformStreamDefaultSinkCloseAlgorithm(stream); - } - - stream[_writable] = createWritableStream( - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - writableHighWaterMark, - writableSizeAlgorithm, - ); - - function pullAlgorithm() { - return transformStreamDefaultSourcePullAlgorithm(stream); - } - - function cancelAlgorithm(reason) { - transformStreamErrorWritableAndUnblockWrite(stream, reason); - return resolvePromiseWith(undefined); - } - - stream[_readable] = createReadableStream( - startAlgorithm, - pullAlgorithm, - cancelAlgorithm, - readableHighWaterMark, - readableSizeAlgorithm, - ); - - stream[_backpressure] = stream[_backpressureChangePromise] = undefined; - transformStreamSetBackpressure(stream, true); - stream[_controller] = undefined; - } + ); - /** @param {WritableStream} stream */ - function initializeWritableStream(stream) { - stream[_state] = "writable"; - stream[_storedError] = - stream[_writer] = - stream[_controller] = - stream[_inFlightWriteRequest] = - stream[_closeRequest] = - stream[_inFlightCloseRequest] = - stream[_pendingAbortRequest] = - undefined; - stream[_writeRequests] = []; - stream[_backpressure] = false; + function pullAlgorithm() { + return transformStreamDefaultSourcePullAlgorithm(stream); } - /** - * @param {unknown} v - * @returns {v is number} - */ - function isNonNegativeNumber(v) { - if (typeof v !== "number") { - return false; - } - if (NumberIsNaN(v)) { - return false; - } - if (v < 0) { - return false; - } - return true; + function cancelAlgorithm(reason) { + transformStreamErrorWritableAndUnblockWrite(stream, reason); + return resolvePromiseWith(undefined); } - /** - * @param {unknown} value - * @returns {value is ReadableStream} - */ - function isReadableStream(value) { - return !(typeof value !== "object" || value === null || - !ReflectHas(value, _controller)); - } + stream[_readable] = createReadableStream( + startAlgorithm, + pullAlgorithm, + cancelAlgorithm, + readableHighWaterMark, + readableSizeAlgorithm, + ); - /** - * @param {ReadableStream} stream - * @returns {boolean} - */ - function isReadableStreamLocked(stream) { - if (stream[_reader] === undefined) { - return false; - } - return true; + stream[_backpressure] = stream[_backpressureChangePromise] = undefined; + transformStreamSetBackpressure(stream, true); + stream[_controller] = undefined; +} + +/** @param {WritableStream} stream */ +function initializeWritableStream(stream) { + stream[_state] = "writable"; + stream[_storedError] = + stream[_writer] = + stream[_controller] = + stream[_inFlightWriteRequest] = + stream[_closeRequest] = + stream[_inFlightCloseRequest] = + stream[_pendingAbortRequest] = + undefined; + stream[_writeRequests] = []; + stream[_backpressure] = false; +} + +/** + * @param {unknown} v + * @returns {v is number} + */ +function isNonNegativeNumber(v) { + if (typeof v !== "number") { + return false; } - - /** - * @param {unknown} value - * @returns {value is ReadableStreamDefaultReader} - */ - function isReadableStreamDefaultReader(value) { - return !(typeof value !== "object" || value === null || - !ReflectHas(value, _readRequests)); + if (NumberIsNaN(v)) { + return false; } - - /** - * @param {unknown} value - * @returns {value is ReadableStreamBYOBReader} - */ - function isReadableStreamBYOBReader(value) { - return !(typeof value !== "object" || value === null || - !ReflectHas(value, _readIntoRequests)); + if (v < 0) { + return false; } - - /** - * @param {ReadableStream} stream - * @returns {boolean} - */ - function isReadableStreamDisturbed(stream) { - assert(isReadableStream(stream)); - return stream[_disturbed]; + return true; +} + +/** + * @param {unknown} value + * @returns {value is ReadableStream} + */ +function isReadableStream(value) { + return !(typeof value !== "object" || value === null || + !ReflectHas(value, _controller)); +} + +/** + * @param {ReadableStream} stream + * @returns {boolean} + */ +function isReadableStreamLocked(stream) { + if (stream[_reader] === undefined) { + return false; } - - const DEFAULT_CHUNK_SIZE = 64 * 1024; // 64 KiB - - // A finalization registry to clean up underlying resources that are GC'ed. - const RESOURCE_REGISTRY = new FinalizationRegistry((rid) => { + return true; +} + +/** + * @param {unknown} value + * @returns {value is ReadableStreamDefaultReader} + */ +function isReadableStreamDefaultReader(value) { + return !(typeof value !== "object" || value === null || + !ReflectHas(value, _readRequests)); +} + +/** + * @param {unknown} value + * @returns {value is ReadableStreamBYOBReader} + */ +function isReadableStreamBYOBReader(value) { + return !(typeof value !== "object" || value === null || + !ReflectHas(value, _readIntoRequests)); +} + +/** + * @param {ReadableStream} stream + * @returns {boolean} + */ +function isReadableStreamDisturbed(stream) { + assert(isReadableStream(stream)); + return stream[_disturbed]; +} + +const DEFAULT_CHUNK_SIZE = 64 * 1024; // 64 KiB + +// A finalization registry to clean up underlying resources that are GC'ed. +const RESOURCE_REGISTRY = new FinalizationRegistry((rid) => { + core.tryClose(rid); +}); + +const _readAll = Symbol("[[readAll]]"); +const _original = Symbol("[[original]]"); +/** + * Create a new ReadableStream object that is backed by a Resource that + * implements `Resource::read_return`. This object contains enough metadata to + * allow callers to bypass the JavaScript ReadableStream implementation and + * read directly from the underlying resource if they so choose (FastStream). + * + * @param {number} rid The resource ID to read from. + * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. + * @returns {ReadableStream<Uint8Array>} + */ +function readableStreamForRid(rid, autoClose = true) { + const stream = webidl.createBranded(ReadableStream); + stream[_resourceBacking] = { rid, autoClose }; + + const tryClose = () => { + if (!autoClose) return; + RESOURCE_REGISTRY.unregister(stream); core.tryClose(rid); - }); - - const _readAll = Symbol("[[readAll]]"); - const _original = Symbol("[[original]]"); - /** - * Create a new ReadableStream object that is backed by a Resource that - * implements `Resource::read_return`. This object contains enough metadata to - * allow callers to bypass the JavaScript ReadableStream implementation and - * read directly from the underlying resource if they so choose (FastStream). - * - * @param {number} rid The resource ID to read from. - * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. - * @returns {ReadableStream<Uint8Array>} - */ - function readableStreamForRid(rid, autoClose = true) { - const stream = webidl.createBranded(ReadableStream); - stream[_resourceBacking] = { rid, autoClose }; - - const tryClose = () => { - if (!autoClose) return; - RESOURCE_REGISTRY.unregister(stream); - core.tryClose(rid); - }; + }; - if (autoClose) { - RESOURCE_REGISTRY.register(stream, rid, stream); - } + if (autoClose) { + RESOURCE_REGISTRY.register(stream, rid, stream); + } - const underlyingSource = { - type: "bytes", - async pull(controller) { - const v = controller.byobRequest.view; - try { - if (controller[_readAll] === true) { - // fast path for tee'd streams consuming body - const chunk = await core.readAll(rid); - if (chunk.byteLength > 0) { - controller.enqueue(chunk); - } - controller.close(); - tryClose(); - return; + const underlyingSource = { + type: "bytes", + async pull(controller) { + const v = controller.byobRequest.view; + try { + if (controller[_readAll] === true) { + // fast path for tee'd streams consuming body + const chunk = await core.readAll(rid); + if (chunk.byteLength > 0) { + controller.enqueue(chunk); } + controller.close(); + tryClose(); + return; + } - const bytesRead = await core.read(rid, v); - if (bytesRead === 0) { - tryClose(); - controller.close(); - controller.byobRequest.respond(0); - } else { - controller.byobRequest.respond(bytesRead); - } - } catch (e) { - controller.error(e); + const bytesRead = await core.read(rid, v); + if (bytesRead === 0) { tryClose(); + controller.close(); + controller.byobRequest.respond(0); + } else { + controller.byobRequest.respond(bytesRead); } - }, - cancel() { + } catch (e) { + controller.error(e); tryClose(); - }, - autoAllocateChunkSize: DEFAULT_CHUNK_SIZE, - }; - initializeReadableStream(stream); - setUpReadableByteStreamControllerFromUnderlyingSource( - stream, - underlyingSource, - underlyingSource, - 0, - ); - return stream; - } - - const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); - const _isUnref = Symbol("isUnref"); - /** - * Create a new ReadableStream object that is backed by a Resource that - * implements `Resource::read_return`. This readable stream supports being - * refed and unrefed by calling `readableStreamForRidUnrefableRef` and - * `readableStreamForRidUnrefableUnref` on it. Unrefable streams are not - * FastStream compatible. - * - * @param {number} rid The resource ID to read from. - * @returns {ReadableStream<Uint8Array>} - */ - function readableStreamForRidUnrefable(rid) { - const stream = webidl.createBranded(ReadableStream); - stream[promiseIdSymbol] = undefined; - stream[_isUnref] = false; - stream[_resourceBackingUnrefable] = { rid, autoClose: true }; - const underlyingSource = { - type: "bytes", - async pull(controller) { - const v = controller.byobRequest.view; - try { - const promise = core.read(rid, v); - const promiseId = stream[promiseIdSymbol] = promise[promiseIdSymbol]; - if (stream[_isUnref]) core.unrefOp(promiseId); - const bytesRead = await promise; - stream[promiseIdSymbol] = undefined; - if (bytesRead === 0) { - core.tryClose(rid); - controller.close(); - controller.byobRequest.respond(0); - } else { - controller.byobRequest.respond(bytesRead); - } - } catch (e) { - controller.error(e); + } + }, + cancel() { + tryClose(); + }, + autoAllocateChunkSize: DEFAULT_CHUNK_SIZE, + }; + initializeReadableStream(stream); + setUpReadableByteStreamControllerFromUnderlyingSource( + stream, + underlyingSource, + underlyingSource, + 0, + ); + return stream; +} + +const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); +const _isUnref = Symbol("isUnref"); +/** + * Create a new ReadableStream object that is backed by a Resource that + * implements `Resource::read_return`. This readable stream supports being + * refed and unrefed by calling `readableStreamForRidUnrefableRef` and + * `readableStreamForRidUnrefableUnref` on it. Unrefable streams are not + * FastStream compatible. + * + * @param {number} rid The resource ID to read from. + * @returns {ReadableStream<Uint8Array>} + */ +function readableStreamForRidUnrefable(rid) { + const stream = webidl.createBranded(ReadableStream); + stream[promiseIdSymbol] = undefined; + stream[_isUnref] = false; + stream[_resourceBackingUnrefable] = { rid, autoClose: true }; + const underlyingSource = { + type: "bytes", + async pull(controller) { + const v = controller.byobRequest.view; + try { + const promise = core.read(rid, v); + const promiseId = stream[promiseIdSymbol] = promise[promiseIdSymbol]; + if (stream[_isUnref]) core.unrefOp(promiseId); + const bytesRead = await promise; + stream[promiseIdSymbol] = undefined; + if (bytesRead === 0) { core.tryClose(rid); + controller.close(); + controller.byobRequest.respond(0); + } else { + controller.byobRequest.respond(bytesRead); } - }, - cancel() { + } catch (e) { + controller.error(e); core.tryClose(rid); - }, - autoAllocateChunkSize: DEFAULT_CHUNK_SIZE, - }; - initializeReadableStream(stream); - setUpReadableByteStreamControllerFromUnderlyingSource( - stream, - underlyingSource, - underlyingSource, - 0, - ); - return stream; - } + } + }, + cancel() { + core.tryClose(rid); + }, + autoAllocateChunkSize: DEFAULT_CHUNK_SIZE, + }; + initializeReadableStream(stream); + setUpReadableByteStreamControllerFromUnderlyingSource( + stream, + underlyingSource, + underlyingSource, + 0, + ); + return stream; +} - function readableStreamIsUnrefable(stream) { - return ReflectHas(stream, _isUnref); - } +function readableStreamIsUnrefable(stream) { + return ReflectHas(stream, _isUnref); +} - function readableStreamForRidUnrefableRef(stream) { - if (!readableStreamIsUnrefable(stream)) { - throw new TypeError("Not an unrefable stream"); - } - stream[_isUnref] = false; - if (stream[promiseIdSymbol] !== undefined) { - core.refOp(stream[promiseIdSymbol]); - } +function readableStreamForRidUnrefableRef(stream) { + if (!readableStreamIsUnrefable(stream)) { + throw new TypeError("Not an unrefable stream"); } - - function readableStreamForRidUnrefableUnref(stream) { - if (!readableStreamIsUnrefable(stream)) { - throw new TypeError("Not an unrefable stream"); - } - stream[_isUnref] = true; - if (stream[promiseIdSymbol] !== undefined) { - core.unrefOp(stream[promiseIdSymbol]); - } + stream[_isUnref] = false; + if (stream[promiseIdSymbol] !== undefined) { + core.refOp(stream[promiseIdSymbol]); } +} - function getReadableStreamResourceBacking(stream) { - return stream[_resourceBacking]; +function readableStreamForRidUnrefableUnref(stream) { + if (!readableStreamIsUnrefable(stream)) { + throw new TypeError("Not an unrefable stream"); } - - function getReadableStreamResourceBackingUnrefable(stream) { - return stream[_resourceBackingUnrefable]; + stream[_isUnref] = true; + if (stream[promiseIdSymbol] !== undefined) { + core.unrefOp(stream[promiseIdSymbol]); } +} - async function readableStreamCollectIntoUint8Array(stream) { - const resourceBacking = getReadableStreamResourceBacking(stream) || - getReadableStreamResourceBackingUnrefable(stream); - const reader = acquireReadableStreamDefaultReader(stream); - - if (resourceBacking) { - // fast path, read whole body in a single op call - try { - readableStreamDisturb(stream); - const promise = core.opAsync("op_read_all", resourceBacking.rid); - if (readableStreamIsUnrefable(stream)) { - const promiseId = stream[promiseIdSymbol] = promise[promiseIdSymbol]; - if (stream[_isUnref]) core.unrefOp(promiseId); - } - const buf = await promise; - readableStreamThrowIfErrored(stream); - readableStreamClose(stream); - return buf; - } catch (err) { - readableStreamThrowIfErrored(stream); - readableStreamError(stream, err); - throw err; - } finally { - if (resourceBacking.autoClose) { - core.tryClose(resourceBacking.rid); - } - } - } - - // slow path - /** @type {Uint8Array[]} */ - const chunks = []; - let totalLength = 0; +function getReadableStreamResourceBacking(stream) { + return stream[_resourceBacking]; +} - // tee'd stream - if (stream[_original]) { - // One of the branches is consuming the stream - // signal controller.pull that we can consume it in a single op - stream[_original][_controller][_readAll] = true; - } - - while (true) { - const { value: chunk, done } = await reader.read(); +function getReadableStreamResourceBackingUnrefable(stream) { + return stream[_resourceBackingUnrefable]; +} - if (done) break; +async function readableStreamCollectIntoUint8Array(stream) { + const resourceBacking = getReadableStreamResourceBacking(stream) || + getReadableStreamResourceBackingUnrefable(stream); + const reader = acquireReadableStreamDefaultReader(stream); - if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)) { - throw new TypeError( - "Can't convert value to Uint8Array while consuming the stream", - ); + if (resourceBacking) { + // fast path, read whole body in a single op call + try { + readableStreamDisturb(stream); + const promise = core.opAsync("op_read_all", resourceBacking.rid); + if (readableStreamIsUnrefable(stream)) { + const promiseId = stream[promiseIdSymbol] = promise[promiseIdSymbol]; + if (stream[_isUnref]) core.unrefOp(promiseId); + } + const buf = await promise; + readableStreamThrowIfErrored(stream); + readableStreamClose(stream); + return buf; + } catch (err) { + readableStreamThrowIfErrored(stream); + readableStreamError(stream, err); + throw err; + } finally { + if (resourceBacking.autoClose) { + core.tryClose(resourceBacking.rid); } - - ArrayPrototypePush(chunks, chunk); - totalLength += chunk.byteLength; } - - const finalBuffer = new Uint8Array(totalLength); - let offset = 0; - for (let i = 0; i < chunks.length; ++i) { - const chunk = chunks[i]; - TypedArrayPrototypeSet(finalBuffer, chunk, offset); - offset += chunk.byteLength; - } - return finalBuffer; } - /** - * Create a new Writable object that is backed by a Resource that implements - * `Resource::write` / `Resource::write_all`. This object contains enough - * metadata to allow callers to bypass the JavaScript WritableStream - * implementation and write directly to the underlying resource if they so - * choose (FastStream). - * - * @param {number} rid The resource ID to write to. - * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. - * @returns {ReadableStream<Uint8Array>} - */ - function writableStreamForRid(rid, autoClose = true) { - const stream = webidl.createBranded(WritableStream); - stream[_resourceBacking] = { rid, autoClose }; + // slow path + /** @type {Uint8Array[]} */ + const chunks = []; + let totalLength = 0; - const tryClose = () => { - if (!autoClose) return; - RESOURCE_REGISTRY.unregister(stream); - core.tryClose(rid); - }; - - if (autoClose) { - RESOURCE_REGISTRY.register(stream, rid, stream); - } - - const underlyingSink = { - async write(chunk, controller) { - try { - await core.writeAll(rid, chunk); - } catch (e) { - controller.error(e); - tryClose(); - } - }, - close() { - tryClose(); - }, - abort() { - tryClose(); - }, - }; - initializeWritableStream(stream); - setUpWritableStreamDefaultControllerFromUnderlyingSink( - stream, - underlyingSink, - underlyingSink, - 1, - () => 1, - ); - return stream; + // tee'd stream + if (stream[_original]) { + // One of the branches is consuming the stream + // signal controller.pull that we can consume it in a single op + stream[_original][_controller][_readAll] = true; } - function getWritableStreamResourceBacking(stream) { - return stream[_resourceBacking]; - } + while (true) { + const { value: chunk, done } = await reader.read(); - /* - * @param {ReadableStream} stream - */ - function readableStreamThrowIfErrored(stream) { - if (stream[_state] === "errored") { - throw stream[_storedError]; - } - } + if (done) break; - /** - * @param {unknown} value - * @returns {value is WritableStream} - */ - function isWritableStream(value) { - return !(typeof value !== "object" || value === null || - !ReflectHas(value, _controller)); - } - - /** - * @param {WritableStream} stream - * @returns {boolean} - */ - function isWritableStreamLocked(stream) { - if (stream[_writer] === undefined) { - return false; - } - return true; - } - /** - * @template T - * @param {{ [_queue]: Array<ValueWithSize<T | _close>>, [_queueTotalSize]: number }} container - * @returns {T | _close} - */ - function peekQueueValue(container) { - assert( - ReflectHas(container, _queue) && - ReflectHas(container, _queueTotalSize), - ); - assert(container[_queue].length); - const valueWithSize = container[_queue][0]; - return valueWithSize.value; - } - - /** - * @param {ReadableByteStreamController} controller - * @returns {void} - */ - function readableByteStreamControllerCallPullIfNeeded(controller) { - const shouldPull = readableByteStreamControllerShouldCallPull(controller); - if (!shouldPull) { - return; - } - if (controller[_pulling]) { - controller[_pullAgain] = true; - return; + if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)) { + throw new TypeError( + "Can't convert value to Uint8Array while consuming the stream", + ); } - assert(controller[_pullAgain] === false); - controller[_pulling] = true; - /** @type {Promise<void>} */ - const pullPromise = controller[_pullAlgorithm](controller); - setPromiseIsHandledToTrue( - PromisePrototypeThen( - pullPromise, - () => { - controller[_pulling] = false; - if (controller[_pullAgain]) { - controller[_pullAgain] = false; - readableByteStreamControllerCallPullIfNeeded(controller); - } - }, - (e) => { - readableByteStreamControllerError(controller, e); - }, - ), - ); - } - /** - * @param {ReadableByteStreamController} controller - * @returns {void} - */ - function readableByteStreamControllerClearAlgorithms(controller) { - controller[_pullAlgorithm] = undefined; - controller[_cancelAlgorithm] = undefined; - } + ArrayPrototypePush(chunks, chunk); + totalLength += chunk.byteLength; + } + + const finalBuffer = new Uint8Array(totalLength); + let offset = 0; + for (let i = 0; i < chunks.length; ++i) { + const chunk = chunks[i]; + TypedArrayPrototypeSet(finalBuffer, chunk, offset); + offset += chunk.byteLength; + } + return finalBuffer; +} + +/** + * Create a new Writable object that is backed by a Resource that implements + * `Resource::write` / `Resource::write_all`. This object contains enough + * metadata to allow callers to bypass the JavaScript WritableStream + * implementation and write directly to the underlying resource if they so + * choose (FastStream). + * + * @param {number} rid The resource ID to write to. + * @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true. + * @returns {ReadableStream<Uint8Array>} + */ +function writableStreamForRid(rid, autoClose = true) { + const stream = webidl.createBranded(WritableStream); + stream[_resourceBacking] = { rid, autoClose }; + + const tryClose = () => { + if (!autoClose) return; + RESOURCE_REGISTRY.unregister(stream); + core.tryClose(rid); + }; - /** - * @param {ReadableByteStreamController} controller - * @param {any} e - */ - function readableByteStreamControllerError(controller, e) { - /** @type {ReadableStream<ArrayBuffer>} */ - const stream = controller[_stream]; - if (stream[_state] !== "readable") { - return; - } - readableByteStreamControllerClearPendingPullIntos(controller); - resetQueue(controller); - readableByteStreamControllerClearAlgorithms(controller); - readableStreamError(stream, e); + if (autoClose) { + RESOURCE_REGISTRY.register(stream, rid, stream); } - /** - * @param {ReadableByteStreamController} controller - * @returns {void} - */ - function readableByteStreamControllerClearPendingPullIntos(controller) { - readableByteStreamControllerInvalidateBYOBRequest(controller); - controller[_pendingPullIntos] = []; + const underlyingSink = { + async write(chunk, controller) { + try { + await core.writeAll(rid, chunk); + } catch (e) { + controller.error(e); + tryClose(); + } + }, + close() { + tryClose(); + }, + abort() { + tryClose(); + }, + }; + initializeWritableStream(stream); + setUpWritableStreamDefaultControllerFromUnderlyingSink( + stream, + underlyingSink, + underlyingSink, + 1, + () => 1, + ); + return stream; +} + +function getWritableStreamResourceBacking(stream) { + return stream[_resourceBacking]; +} + +/* + * @param {ReadableStream} stream + */ +function readableStreamThrowIfErrored(stream) { + if (stream[_state] === "errored") { + throw stream[_storedError]; + } +} + +/** + * @param {unknown} value + * @returns {value is WritableStream} + */ +function isWritableStream(value) { + return !(typeof value !== "object" || value === null || + !ReflectHas(value, _controller)); +} + +/** + * @param {WritableStream} stream + * @returns {boolean} + */ +function isWritableStreamLocked(stream) { + if (stream[_writer] === undefined) { + return false; } - - /** - * @param {ReadableByteStreamController} controller - * @returns {void} - */ - function readableByteStreamControllerClose(controller) { - /** @type {ReadableStream<ArrayBuffer>} */ - const stream = controller[_stream]; - if (controller[_closeRequested] || stream[_state] !== "readable") { - return; - } - if (controller[_queueTotalSize] > 0) { - controller[_closeRequested] = true; - return; - } - if (controller[_pendingPullIntos].length !== 0) { - const firstPendingPullInto = controller[_pendingPullIntos][0]; - if (firstPendingPullInto.bytesFilled > 0) { - const e = new TypeError( - "Insufficient bytes to fill elements in the given buffer", - ); + return true; +} +/** + * @template T + * @param {{ [_queue]: Array<ValueWithSize<T | _close>>, [_queueTotalSize]: number }} container + * @returns {T | _close} + */ +function peekQueueValue(container) { + assert( + ReflectHas(container, _queue) && + ReflectHas(container, _queueTotalSize), + ); + assert(container[_queue].length); + const valueWithSize = container[_queue][0]; + return valueWithSize.value; +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {void} + */ +function readableByteStreamControllerCallPullIfNeeded(controller) { + const shouldPull = readableByteStreamControllerShouldCallPull(controller); + if (!shouldPull) { + return; + } + if (controller[_pulling]) { + controller[_pullAgain] = true; + return; + } + assert(controller[_pullAgain] === false); + controller[_pulling] = true; + /** @type {Promise<void>} */ + const pullPromise = controller[_pullAlgorithm](controller); + setPromiseIsHandledToTrue( + PromisePrototypeThen( + pullPromise, + () => { + controller[_pulling] = false; + if (controller[_pullAgain]) { + controller[_pullAgain] = false; + readableByteStreamControllerCallPullIfNeeded(controller); + } + }, + (e) => { readableByteStreamControllerError(controller, e); - throw e; - } - } - readableByteStreamControllerClearAlgorithms(controller); - readableStreamClose(stream); + }, + ), + ); +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {void} + */ +function readableByteStreamControllerClearAlgorithms(controller) { + controller[_pullAlgorithm] = undefined; + controller[_cancelAlgorithm] = undefined; +} + +/** + * @param {ReadableByteStreamController} controller + * @param {any} e + */ +function readableByteStreamControllerError(controller, e) { + /** @type {ReadableStream<ArrayBuffer>} */ + const stream = controller[_stream]; + if (stream[_state] !== "readable") { + return; + } + readableByteStreamControllerClearPendingPullIntos(controller); + resetQueue(controller); + readableByteStreamControllerClearAlgorithms(controller); + readableStreamError(stream, e); +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {void} + */ +function readableByteStreamControllerClearPendingPullIntos(controller) { + readableByteStreamControllerInvalidateBYOBRequest(controller); + controller[_pendingPullIntos] = []; +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {void} + */ +function readableByteStreamControllerClose(controller) { + /** @type {ReadableStream<ArrayBuffer>} */ + const stream = controller[_stream]; + if (controller[_closeRequested] || stream[_state] !== "readable") { + return; + } + if (controller[_queueTotalSize] > 0) { + controller[_closeRequested] = true; + return; + } + if (controller[_pendingPullIntos].length !== 0) { + const firstPendingPullInto = controller[_pendingPullIntos][0]; + if (firstPendingPullInto.bytesFilled > 0) { + const e = new TypeError( + "Insufficient bytes to fill elements in the given buffer", + ); + readableByteStreamControllerError(controller, e); + throw e; + } + } + readableByteStreamControllerClearAlgorithms(controller); + readableStreamClose(stream); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {ArrayBufferView} chunk + */ +function readableByteStreamControllerEnqueue(controller, chunk) { + /** @type {ReadableStream<ArrayBuffer>} */ + const stream = controller[_stream]; + if ( + controller[_closeRequested] || + controller[_stream][_state] !== "readable" + ) { + return; } - /** - * @param {ReadableByteStreamController} controller - * @param {ArrayBufferView} chunk - */ - function readableByteStreamControllerEnqueue(controller, chunk) { - /** @type {ReadableStream<ArrayBuffer>} */ - const stream = controller[_stream]; - if ( - controller[_closeRequested] || - controller[_stream][_state] !== "readable" - ) { - return; - } - - const { buffer, byteOffset, byteLength } = chunk; - if (isDetachedBuffer(buffer)) { + const { buffer, byteOffset, byteLength } = chunk; + if (isDetachedBuffer(buffer)) { + throw new TypeError( + "chunk's buffer is detached and so cannot be enqueued", + ); + } + const transferredBuffer = transferArrayBuffer(buffer); + if (controller[_pendingPullIntos].length !== 0) { + const firstPendingPullInto = controller[_pendingPullIntos][0]; + if (isDetachedBuffer(firstPendingPullInto.buffer)) { throw new TypeError( - "chunk's buffer is detached and so cannot be enqueued", + "The BYOB request's buffer has been detached and so cannot be filled with an enqueued chunk", ); } - const transferredBuffer = transferArrayBuffer(buffer); - if (controller[_pendingPullIntos].length !== 0) { - const firstPendingPullInto = controller[_pendingPullIntos][0]; - if (isDetachedBuffer(firstPendingPullInto.buffer)) { - throw new TypeError( - "The BYOB request's buffer has been detached and so cannot be filled with an enqueued chunk", - ); - } - readableByteStreamControllerInvalidateBYOBRequest(controller); - firstPendingPullInto.buffer = transferArrayBuffer( - firstPendingPullInto.buffer, + readableByteStreamControllerInvalidateBYOBRequest(controller); + firstPendingPullInto.buffer = transferArrayBuffer( + firstPendingPullInto.buffer, + ); + if (firstPendingPullInto.readerType === "none") { + readableByteStreamControllerEnqueueDetachedPullIntoToQueue( + controller, + firstPendingPullInto, ); - if (firstPendingPullInto.readerType === "none") { - readableByteStreamControllerEnqueueDetachedPullIntoToQueue( - controller, - firstPendingPullInto, - ); - } } - if (readableStreamHasDefaultReader(stream)) { - readableByteStreamControllerProcessReadRequestsUsingQueue(controller); - if (readableStreamGetNumReadRequests(stream) === 0) { - assert(controller[_pendingPullIntos].length === 0); - readableByteStreamControllerEnqueueChunkToQueue( - controller, - transferredBuffer, - byteOffset, - byteLength, - ); - } else { - assert(controller[_queue].length === 0); - if (controller[_pendingPullIntos].length !== 0) { - assert(controller[_pendingPullIntos][0].readerType === "default"); - readableByteStreamControllerShiftPendingPullInto(controller); - } - const transferredView = new Uint8Array( - transferredBuffer, - byteOffset, - byteLength, - ); - readableStreamFulfillReadRequest(stream, transferredView, false); - } - } else if (readableStreamHasBYOBReader(stream)) { + } + if (readableStreamHasDefaultReader(stream)) { + readableByteStreamControllerProcessReadRequestsUsingQueue(controller); + if (readableStreamGetNumReadRequests(stream) === 0) { + assert(controller[_pendingPullIntos].length === 0); readableByteStreamControllerEnqueueChunkToQueue( controller, transferredBuffer, byteOffset, byteLength, ); - readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( - controller, - ); } else { - assert(isReadableStreamLocked(stream) === false); - readableByteStreamControllerEnqueueChunkToQueue( - controller, + assert(controller[_queue].length === 0); + if (controller[_pendingPullIntos].length !== 0) { + assert(controller[_pendingPullIntos][0].readerType === "default"); + readableByteStreamControllerShiftPendingPullInto(controller); + } + const transferredView = new Uint8Array( transferredBuffer, byteOffset, byteLength, ); + readableStreamFulfillReadRequest(stream, transferredView, false); } - readableByteStreamControllerCallPullIfNeeded(controller); + } else if (readableStreamHasBYOBReader(stream)) { + readableByteStreamControllerEnqueueChunkToQueue( + controller, + transferredBuffer, + byteOffset, + byteLength, + ); + readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( + controller, + ); + } else { + assert(isReadableStreamLocked(stream) === false); + readableByteStreamControllerEnqueueChunkToQueue( + controller, + transferredBuffer, + byteOffset, + byteLength, + ); } - - /** - * @param {ReadableByteStreamController} controller - * @param {ArrayBufferLike} buffer - * @param {number} byteOffset - * @param {number} byteLength - * @returns {void} - */ - function readableByteStreamControllerEnqueueChunkToQueue( + readableByteStreamControllerCallPullIfNeeded(controller); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {ArrayBufferLike} buffer + * @param {number} byteOffset + * @param {number} byteLength + * @returns {void} + */ +function readableByteStreamControllerEnqueueChunkToQueue( + controller, + buffer, + byteOffset, + byteLength, +) { + ArrayPrototypePush(controller[_queue], { buffer, byteOffset, byteLength }); + controller[_queueTotalSize] += byteLength; +} + +/** + * @param {ReadableByteStreamController} controller + * @param {ArrayBufferLike} buffer + * @param {number} byteOffset + * @param {number} byteLength + * @returns {void} + */ +function readableByteStreamControllerEnqueueClonedChunkToQueue( + controller, + buffer, + byteOffset, + byteLength, +) { + let cloneResult; + try { + cloneResult = buffer.slice(byteOffset, byteOffset + byteLength); + } catch (e) { + readableByteStreamControllerError(controller, e); + } + readableByteStreamControllerEnqueueChunkToQueue( controller, - buffer, - byteOffset, + cloneResult, + 0, byteLength, - ) { - ArrayPrototypePush(controller[_queue], { buffer, byteOffset, byteLength }); - controller[_queueTotalSize] += byteLength; + ); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {PullIntoDescriptor} pullIntoDescriptor + * @returns {void} + */ +function readableByteStreamControllerEnqueueDetachedPullIntoToQueue( + controller, + pullIntoDescriptor, +) { + assert(pullIntoDescriptor.readerType === "none"); + if (pullIntoDescriptor.bytesFilled > 0) { + readableByteStreamControllerEnqueueClonedChunkToQueue( + controller, + pullIntoDescriptor.buffer, + pullIntoDescriptor.byteOffset, + pullIntoDescriptor.bytesFilled, + ); } - - /** - * @param {ReadableByteStreamController} controller - * @param {ArrayBufferLike} buffer - * @param {number} byteOffset - * @param {number} byteLength - * @returns {void} - */ - function readableByteStreamControllerEnqueueClonedChunkToQueue( - controller, - buffer, - byteOffset, - byteLength, + readableByteStreamControllerShiftPendingPullInto(controller); +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {ReadableStreamBYOBRequest | null} + */ +function readableByteStreamControllerGetBYOBRequest(controller) { + if ( + controller[_byobRequest] === null && + controller[_pendingPullIntos].length !== 0 ) { - let cloneResult; - try { - cloneResult = buffer.slice(byteOffset, byteOffset + byteLength); - } catch (e) { - readableByteStreamControllerError(controller, e); - } - readableByteStreamControllerEnqueueChunkToQueue( - controller, - cloneResult, - 0, - byteLength, + const firstDescriptor = controller[_pendingPullIntos][0]; + const view = new Uint8Array( + firstDescriptor.buffer, + firstDescriptor.byteOffset + firstDescriptor.bytesFilled, + firstDescriptor.byteLength - firstDescriptor.bytesFilled, ); + const byobRequest = webidl.createBranded(ReadableStreamBYOBRequest); + byobRequest[_controller] = controller; + byobRequest[_view] = view; + controller[_byobRequest] = byobRequest; + } + return controller[_byobRequest]; +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {number | null} + */ +function readableByteStreamControllerGetDesiredSize(controller) { + const state = controller[_stream][_state]; + if (state === "errored") { + return null; + } + if (state === "closed") { + return 0; + } + return controller[_strategyHWM] - controller[_queueTotalSize]; +} + +/** + * @param {{ [_queue]: any[], [_queueTotalSize]: number }} container + * @returns {void} + */ +function resetQueue(container) { + container[_queue] = []; + container[_queueTotalSize] = 0; +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {void} + */ +function readableByteStreamControllerHandleQueueDrain(controller) { + assert(controller[_stream][_state] === "readable"); + if ( + controller[_queueTotalSize] === 0 && controller[_closeRequested] + ) { + readableByteStreamControllerClearAlgorithms(controller); + readableStreamClose(controller[_stream]); + } else { + readableByteStreamControllerCallPullIfNeeded(controller); } - - /** - * @param {ReadableByteStreamController} controller - * @param {PullIntoDescriptor} pullIntoDescriptor - * @returns {void} - */ - function readableByteStreamControllerEnqueueDetachedPullIntoToQueue( - controller, - pullIntoDescriptor, +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {boolean} + */ +function readableByteStreamControllerShouldCallPull(controller) { + /** @type {ReadableStream<ArrayBuffer>} */ + const stream = controller[_stream]; + if ( + stream[_state] !== "readable" || + controller[_closeRequested] || + !controller[_started] ) { - assert(pullIntoDescriptor.readerType === "none"); - if (pullIntoDescriptor.bytesFilled > 0) { - readableByteStreamControllerEnqueueClonedChunkToQueue( - controller, - pullIntoDescriptor.buffer, - pullIntoDescriptor.byteOffset, - pullIntoDescriptor.bytesFilled, - ); - } - readableByteStreamControllerShiftPendingPullInto(controller); + return false; } - - /** - * @param {ReadableByteStreamController} controller - * @returns {ReadableStreamBYOBRequest | null} - */ - function readableByteStreamControllerGetBYOBRequest(controller) { - if ( - controller[_byobRequest] === null && - controller[_pendingPullIntos].length !== 0 - ) { - const firstDescriptor = controller[_pendingPullIntos][0]; - const view = new Uint8Array( - firstDescriptor.buffer, - firstDescriptor.byteOffset + firstDescriptor.bytesFilled, - firstDescriptor.byteLength - firstDescriptor.bytesFilled, - ); - const byobRequest = webidl.createBranded(ReadableStreamBYOBRequest); - byobRequest[_controller] = controller; - byobRequest[_view] = view; - controller[_byobRequest] = byobRequest; - } - return controller[_byobRequest]; + if ( + readableStreamHasDefaultReader(stream) && + readableStreamGetNumReadRequests(stream) > 0 + ) { + return true; } - - /** - * @param {ReadableByteStreamController} controller - * @returns {number | null} - */ - function readableByteStreamControllerGetDesiredSize(controller) { - const state = controller[_stream][_state]; - if (state === "errored") { - return null; - } - if (state === "closed") { - return 0; - } - return controller[_strategyHWM] - controller[_queueTotalSize]; + if ( + readableStreamHasBYOBReader(stream) && + readableStreamGetNumReadIntoRequests(stream) > 0 + ) { + return true; } - - /** - * @param {{ [_queue]: any[], [_queueTotalSize]: number }} container - * @returns {void} - */ - function resetQueue(container) { - container[_queue] = []; - container[_queueTotalSize] = 0; + const desiredSize = readableByteStreamControllerGetDesiredSize(controller); + assert(desiredSize !== null); + return desiredSize > 0; +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {ReadRequest<R>} readRequest + * @returns {void} + */ +function readableStreamAddReadRequest(stream, readRequest) { + assert(isReadableStreamDefaultReader(stream[_reader])); + assert(stream[_state] === "readable"); + ArrayPrototypePush(stream[_reader][_readRequests], readRequest); +} + +/** + * @param {ReadableStream} stream + * @param {ReadIntoRequest} readRequest + * @returns {void} + */ +function readableStreamAddReadIntoRequest(stream, readRequest) { + assert(isReadableStreamBYOBReader(stream[_reader])); + assert(stream[_state] === "readable" || stream[_state] === "closed"); + ArrayPrototypePush(stream[_reader][_readIntoRequests], readRequest); +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {any=} reason + * @returns {Promise<void>} + */ +function readableStreamCancel(stream, reason) { + stream[_disturbed] = true; + if (stream[_state] === "closed") { + return resolvePromiseWith(undefined); } - - /** - * @param {ReadableByteStreamController} controller - * @returns {void} - */ - function readableByteStreamControllerHandleQueueDrain(controller) { - assert(controller[_stream][_state] === "readable"); - if ( - controller[_queueTotalSize] === 0 && controller[_closeRequested] - ) { - readableByteStreamControllerClearAlgorithms(controller); - readableStreamClose(controller[_stream]); - } else { - readableByteStreamControllerCallPullIfNeeded(controller); + if (stream[_state] === "errored") { + return PromiseReject(stream[_storedError]); + } + readableStreamClose(stream); + const reader = stream[_reader]; + if (reader !== undefined && isReadableStreamBYOBReader(reader)) { + const readIntoRequests = reader[_readIntoRequests]; + reader[_readIntoRequests] = []; + for (let i = 0; i < readIntoRequests.length; ++i) { + const readIntoRequest = readIntoRequests[i]; + readIntoRequest.closeSteps(undefined); + } + } + /** @type {Promise<void>} */ + const sourceCancelPromise = stream[_controller][_cancelSteps](reason); + return PromisePrototypeThen(sourceCancelPromise, () => undefined); +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @returns {void} + */ +function readableStreamClose(stream) { + assert(stream[_state] === "readable"); + stream[_state] = "closed"; + /** @type {ReadableStreamDefaultReader<R> | undefined} */ + const reader = stream[_reader]; + if (!reader) { + return; + } + if (isReadableStreamDefaultReader(reader)) { + /** @type {Array<ReadRequest<R>>} */ + const readRequests = reader[_readRequests]; + reader[_readRequests] = []; + for (let i = 0; i < readRequests.length; ++i) { + const readRequest = readRequests[i]; + readRequest.closeSteps(); } } + // This promise can be double resolved. + // See: https://github.com/whatwg/streams/issues/1100 + reader[_closedPromise].resolve(undefined); +} - /** - * @param {ReadableByteStreamController} controller - * @returns {boolean} - */ - function readableByteStreamControllerShouldCallPull(controller) { - /** @type {ReadableStream<ArrayBuffer>} */ - const stream = controller[_stream]; - if ( - stream[_state] !== "readable" || - controller[_closeRequested] || - !controller[_started] - ) { - return false; - } - if ( - readableStreamHasDefaultReader(stream) && - readableStreamGetNumReadRequests(stream) > 0 - ) { - return true; - } - if ( - readableStreamHasBYOBReader(stream) && - readableStreamGetNumReadIntoRequests(stream) > 0 - ) { - return true; +/** + * @template R + * @param {ReadableStream<R>} stream + * @returns {void} + */ +function readableStreamDisturb(stream) { + stream[_disturbed] = true; +} + +/** @param {ReadableStreamDefaultController<any>} controller */ +function readableStreamDefaultControllerCallPullIfNeeded(controller) { + const shouldPull = readableStreamDefaultcontrollerShouldCallPull( + controller, + ); + if (shouldPull === false) { + return; + } + if (controller[_pulling] === true) { + controller[_pullAgain] = true; + return; + } + assert(controller[_pullAgain] === false); + controller[_pulling] = true; + const pullPromise = controller[_pullAlgorithm](controller); + uponFulfillment(pullPromise, () => { + controller[_pulling] = false; + if (controller[_pullAgain] === true) { + controller[_pullAgain] = false; + readableStreamDefaultControllerCallPullIfNeeded(controller); } - const desiredSize = readableByteStreamControllerGetDesiredSize(controller); - assert(desiredSize !== null); - return desiredSize > 0; + }); + uponRejection(pullPromise, (e) => { + readableStreamDefaultControllerError(controller, e); + }); +} + +/** + * @param {ReadableStreamDefaultController<any>} controller + * @returns {boolean} + */ +function readableStreamDefaultControllerCanCloseOrEnqueue(controller) { + const state = controller[_stream][_state]; + if (controller[_closeRequested] === false && state === "readable") { + return true; + } else { + return false; } +} - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {ReadRequest<R>} readRequest - * @returns {void} - */ - function readableStreamAddReadRequest(stream, readRequest) { - assert(isReadableStreamDefaultReader(stream[_reader])); - assert(stream[_state] === "readable"); - ArrayPrototypePush(stream[_reader][_readRequests], readRequest); - } +/** @param {ReadableStreamDefaultController<any>} controller */ +function readableStreamDefaultControllerClearAlgorithms(controller) { + controller[_pullAlgorithm] = undefined; + controller[_cancelAlgorithm] = undefined; + controller[_strategySizeAlgorithm] = undefined; +} - /** - * @param {ReadableStream} stream - * @param {ReadIntoRequest} readRequest - * @returns {void} - */ - function readableStreamAddReadIntoRequest(stream, readRequest) { - assert(isReadableStreamBYOBReader(stream[_reader])); - assert(stream[_state] === "readable" || stream[_state] === "closed"); - ArrayPrototypePush(stream[_reader][_readIntoRequests], readRequest); +/** @param {ReadableStreamDefaultController<any>} controller */ +function readableStreamDefaultControllerClose(controller) { + if ( + readableStreamDefaultControllerCanCloseOrEnqueue(controller) === false + ) { + return; } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {any=} reason - * @returns {Promise<void>} - */ - function readableStreamCancel(stream, reason) { - stream[_disturbed] = true; - if (stream[_state] === "closed") { - return resolvePromiseWith(undefined); - } - if (stream[_state] === "errored") { - return PromiseReject(stream[_storedError]); - } + const stream = controller[_stream]; + controller[_closeRequested] = true; + if (controller[_queue].length === 0) { + readableStreamDefaultControllerClearAlgorithms(controller); readableStreamClose(stream); - const reader = stream[_reader]; - if (reader !== undefined && isReadableStreamBYOBReader(reader)) { - const readIntoRequests = reader[_readIntoRequests]; - reader[_readIntoRequests] = []; - for (let i = 0; i < readIntoRequests.length; ++i) { - const readIntoRequest = readIntoRequests[i]; - readIntoRequest.closeSteps(undefined); - } - } - /** @type {Promise<void>} */ - const sourceCancelPromise = stream[_controller][_cancelSteps](reason); - return PromisePrototypeThen(sourceCancelPromise, () => undefined); } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @returns {void} - */ - function readableStreamClose(stream) { - assert(stream[_state] === "readable"); - stream[_state] = "closed"; - /** @type {ReadableStreamDefaultReader<R> | undefined} */ - const reader = stream[_reader]; - if (!reader) { - return; - } - if (isReadableStreamDefaultReader(reader)) { - /** @type {Array<ReadRequest<R>>} */ - const readRequests = reader[_readRequests]; - reader[_readRequests] = []; - for (let i = 0; i < readRequests.length; ++i) { - const readRequest = readRequests[i]; - readRequest.closeSteps(); - } - } - // This promise can be double resolved. - // See: https://github.com/whatwg/streams/issues/1100 - reader[_closedPromise].resolve(undefined); - } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @returns {void} - */ - function readableStreamDisturb(stream) { - stream[_disturbed] = true; +} + +/** + * @template R + * @param {ReadableStreamDefaultController<R>} controller + * @param {R} chunk + * @returns {void} + */ +function readableStreamDefaultControllerEnqueue(controller, chunk) { + if ( + readableStreamDefaultControllerCanCloseOrEnqueue(controller) === false + ) { + return; } - - /** @param {ReadableStreamDefaultController<any>} controller */ - function readableStreamDefaultControllerCallPullIfNeeded(controller) { - const shouldPull = readableStreamDefaultcontrollerShouldCallPull( - controller, - ); - if (shouldPull === false) { - return; - } - if (controller[_pulling] === true) { - controller[_pullAgain] = true; - return; + const stream = controller[_stream]; + if ( + isReadableStreamLocked(stream) === true && + readableStreamGetNumReadRequests(stream) > 0 + ) { + readableStreamFulfillReadRequest(stream, chunk, false); + } else { + let chunkSize; + try { + chunkSize = controller[_strategySizeAlgorithm](chunk); + } catch (e) { + readableStreamDefaultControllerError(controller, e); + throw e; } - assert(controller[_pullAgain] === false); - controller[_pulling] = true; - const pullPromise = controller[_pullAlgorithm](controller); - uponFulfillment(pullPromise, () => { - controller[_pulling] = false; - if (controller[_pullAgain] === true) { - controller[_pullAgain] = false; - readableStreamDefaultControllerCallPullIfNeeded(controller); - } - }); - uponRejection(pullPromise, (e) => { + + try { + enqueueValueWithSize(controller, chunk, chunkSize); + } catch (e) { readableStreamDefaultControllerError(controller, e); - }); + throw e; + } + } + readableStreamDefaultControllerCallPullIfNeeded(controller); +} + +/** + * @param {ReadableStreamDefaultController<any>} controller + * @param {any} e + */ +function readableStreamDefaultControllerError(controller, e) { + const stream = controller[_stream]; + if (stream[_state] !== "readable") { + return; + } + resetQueue(controller); + readableStreamDefaultControllerClearAlgorithms(controller); + readableStreamError(stream, e); +} + +/** + * @param {ReadableStreamDefaultController<any>} controller + * @returns {number | null} + */ +function readableStreamDefaultControllerGetDesiredSize(controller) { + const state = controller[_stream][_state]; + if (state === "errored") { + return null; + } + if (state === "closed") { + return 0; + } + return controller[_strategyHWM] - controller[_queueTotalSize]; +} + +/** @param {ReadableStreamDefaultController} controller */ +function readableStreamDefaultcontrollerHasBackpressure(controller) { + if (readableStreamDefaultcontrollerShouldCallPull(controller) === true) { + return false; + } else { + return true; } +} - /** - * @param {ReadableStreamDefaultController<any>} controller - * @returns {boolean} - */ - function readableStreamDefaultControllerCanCloseOrEnqueue(controller) { - const state = controller[_stream][_state]; - if (controller[_closeRequested] === false && state === "readable") { - return true; - } else { - return false; - } +/** + * @param {ReadableStreamDefaultController<any>} controller + * @returns {boolean} + */ +function readableStreamDefaultcontrollerShouldCallPull(controller) { + const stream = controller[_stream]; + if ( + readableStreamDefaultControllerCanCloseOrEnqueue(controller) === false + ) { + return false; } - - /** @param {ReadableStreamDefaultController<any>} controller */ - function readableStreamDefaultControllerClearAlgorithms(controller) { - controller[_pullAlgorithm] = undefined; - controller[_cancelAlgorithm] = undefined; - controller[_strategySizeAlgorithm] = undefined; + if (controller[_started] === false) { + return false; } - - /** @param {ReadableStreamDefaultController<any>} controller */ - function readableStreamDefaultControllerClose(controller) { - if ( - readableStreamDefaultControllerCanCloseOrEnqueue(controller) === false - ) { - return; - } - const stream = controller[_stream]; - controller[_closeRequested] = true; - if (controller[_queue].length === 0) { - readableStreamDefaultControllerClearAlgorithms(controller); - readableStreamClose(stream); - } + if ( + isReadableStreamLocked(stream) && + readableStreamGetNumReadRequests(stream) > 0 + ) { + return true; } - - /** - * @template R - * @param {ReadableStreamDefaultController<R>} controller - * @param {R} chunk - * @returns {void} - */ - function readableStreamDefaultControllerEnqueue(controller, chunk) { - if ( - readableStreamDefaultControllerCanCloseOrEnqueue(controller) === false - ) { + const desiredSize = readableStreamDefaultControllerGetDesiredSize( + controller, + ); + assert(desiredSize !== null); + if (desiredSize > 0) { + return true; + } + return false; +} + +/** + * @param {ReadableStreamBYOBReader} reader + * @param {ArrayBufferView} view + * @param {ReadIntoRequest} readIntoRequest + * @returns {void} + */ +function readableStreamBYOBReaderRead(reader, view, readIntoRequest) { + const stream = reader[_stream]; + assert(stream); + stream[_disturbed] = true; + if (stream[_state] === "errored") { + readIntoRequest.errorSteps(stream[_storedError]); + } else { + readableByteStreamControllerPullInto( + stream[_controller], + view, + readIntoRequest, + ); + } +} + +/** + * @param {ReadableStreamBYOBReader} reader + */ +function readableStreamBYOBReaderRelease(reader) { + readableStreamReaderGenericRelease(reader); + const e = new TypeError("The reader was released."); + readableStreamBYOBReaderErrorReadIntoRequests(reader, e); +} + +/** + * @param {ReadableStreamBYOBReader} reader + * @param {any} e + */ +function readableStreamDefaultReaderErrorReadRequests(reader, e) { + const readRequests = reader[_readRequests]; + reader[_readRequests] = []; + for (let i = 0; i < readRequests.length; ++i) { + const readRequest = readRequests[i]; + readRequest.errorSteps(e); + } +} + +/** + * @param {ReadableByteStreamController} controller + */ +function readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( + controller, +) { + assert(!controller[_closeRequested]); + while (controller[_pendingPullIntos].length !== 0) { + if (controller[_queueTotalSize] === 0) { return; } - const stream = controller[_stream]; + const pullIntoDescriptor = controller[_pendingPullIntos][0]; if ( - isReadableStreamLocked(stream) === true && - readableStreamGetNumReadRequests(stream) > 0 + readableByteStreamControllerFillPullIntoDescriptorFromQueue( + controller, + pullIntoDescriptor, + ) ) { - readableStreamFulfillReadRequest(stream, chunk, false); - } else { - let chunkSize; - try { - chunkSize = controller[_strategySizeAlgorithm](chunk); - } catch (e) { - readableStreamDefaultControllerError(controller, e); - throw e; - } - - try { - enqueueValueWithSize(controller, chunk, chunkSize); - } catch (e) { - readableStreamDefaultControllerError(controller, e); - throw e; - } + readableByteStreamControllerShiftPendingPullInto(controller); + readableByteStreamControllerCommitPullIntoDescriptor( + controller[_stream], + pullIntoDescriptor, + ); } - readableStreamDefaultControllerCallPullIfNeeded(controller); } - - /** - * @param {ReadableStreamDefaultController<any>} controller - * @param {any} e - */ - function readableStreamDefaultControllerError(controller, e) { - const stream = controller[_stream]; - if (stream[_state] !== "readable") { +} +/** + * @param {ReadableByteStreamController} controller + */ +function readableByteStreamControllerProcessReadRequestsUsingQueue( + controller, +) { + const reader = controller[_stream][_reader]; + assert(isReadableStreamDefaultReader(reader)); + while (reader[_readRequests].length !== 0) { + if (controller[_queueTotalSize] === 0) { return; } - resetQueue(controller); - readableStreamDefaultControllerClearAlgorithms(controller); - readableStreamError(stream, e); - } - - /** - * @param {ReadableStreamDefaultController<any>} controller - * @returns {number | null} - */ - function readableStreamDefaultControllerGetDesiredSize(controller) { - const state = controller[_stream][_state]; - if (state === "errored") { - return null; - } - if (state === "closed") { - return 0; - } - return controller[_strategyHWM] - controller[_queueTotalSize]; - } - - /** @param {ReadableStreamDefaultController} controller */ - function readableStreamDefaultcontrollerHasBackpressure(controller) { - if (readableStreamDefaultcontrollerShouldCallPull(controller) === true) { - return false; - } else { - return true; - } - } - - /** - * @param {ReadableStreamDefaultController<any>} controller - * @returns {boolean} - */ - function readableStreamDefaultcontrollerShouldCallPull(controller) { - const stream = controller[_stream]; - if ( - readableStreamDefaultControllerCanCloseOrEnqueue(controller) === false - ) { - return false; - } - if (controller[_started] === false) { - return false; - } - if ( - isReadableStreamLocked(stream) && - readableStreamGetNumReadRequests(stream) > 0 - ) { - return true; - } - const desiredSize = readableStreamDefaultControllerGetDesiredSize( + const readRequest = ArrayPrototypeShift(reader[_readRequests]); + readableByteStreamControllerFillReadRequestFromQueue( controller, + readRequest, ); - assert(desiredSize !== null); - if (desiredSize > 0) { - return true; - } - return false; } - - /** - * @param {ReadableStreamBYOBReader} reader - * @param {ArrayBufferView} view - * @param {ReadIntoRequest} readIntoRequest - * @returns {void} - */ - function readableStreamBYOBReaderRead(reader, view, readIntoRequest) { - const stream = reader[_stream]; - assert(stream); - stream[_disturbed] = true; - if (stream[_state] === "errored") { - readIntoRequest.errorSteps(stream[_storedError]); - } else { - readableByteStreamControllerPullInto( - stream[_controller], - view, - readIntoRequest, - ); - } +} + +/** + * @param {ReadableByteStreamController} controller + * @param {ArrayBufferView} view + * @param {ReadIntoRequest} readIntoRequest + * @returns {void} + */ +function readableByteStreamControllerPullInto( + controller, + view, + readIntoRequest, +) { + const stream = controller[_stream]; + let elementSize = 1; + let ctor = DataView; + + if ( + ObjectPrototypeIsPrototypeOf(Int8ArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(Uint8ClampedArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(Int16ArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(Uint16ArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(Int32ArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(Uint32ArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(BigInt64ArrayPrototype, view) || + ObjectPrototypeIsPrototypeOf(BigUint64ArrayPrototype, view) + ) { + elementSize = view.constructor.BYTES_PER_ELEMENT; + ctor = view.constructor; } + const byteOffset = view.byteOffset; + const byteLength = view.byteLength; - /** - * @param {ReadableStreamBYOBReader} reader - */ - function readableStreamBYOBReaderRelease(reader) { - readableStreamReaderGenericRelease(reader); - const e = new TypeError("The reader was released."); - readableStreamBYOBReaderErrorReadIntoRequests(reader, e); - } + /** @type {ArrayBufferLike} */ + let buffer; - /** - * @param {ReadableStreamBYOBReader} reader - * @param {any} e - */ - function readableStreamDefaultReaderErrorReadRequests(reader, e) { - const readRequests = reader[_readRequests]; - reader[_readRequests] = []; - for (let i = 0; i < readRequests.length; ++i) { - const readRequest = readRequests[i]; - readRequest.errorSteps(e); - } + try { + buffer = transferArrayBuffer(view.buffer); + } catch (e) { + readIntoRequest.errorSteps(e); + return; } - /** - * @param {ReadableByteStreamController} controller - */ - function readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( - controller, - ) { - assert(!controller[_closeRequested]); - while (controller[_pendingPullIntos].length !== 0) { - if (controller[_queueTotalSize] === 0) { - return; - } - const pullIntoDescriptor = controller[_pendingPullIntos][0]; - if ( - readableByteStreamControllerFillPullIntoDescriptorFromQueue( - controller, - pullIntoDescriptor, - ) - ) { - readableByteStreamControllerShiftPendingPullInto(controller); - readableByteStreamControllerCommitPullIntoDescriptor( - controller[_stream], - pullIntoDescriptor, - ); - } - } + /** @type {PullIntoDescriptor} */ + const pullIntoDescriptor = { + buffer, + bufferByteLength: buffer.byteLength, + byteOffset, + byteLength, + bytesFilled: 0, + elementSize, + viewConstructor: ctor, + readerType: "byob", + }; + + if (controller[_pendingPullIntos].length !== 0) { + ArrayPrototypePush(controller[_pendingPullIntos], pullIntoDescriptor); + readableStreamAddReadIntoRequest(stream, readIntoRequest); + return; } - /** - * @param {ReadableByteStreamController} controller - */ - function readableByteStreamControllerProcessReadRequestsUsingQueue( - controller, - ) { - const reader = controller[_stream][_reader]; - assert(isReadableStreamDefaultReader(reader)); - while (reader[_readRequests].length !== 0) { - if (controller[_queueTotalSize] === 0) { - return; - } - const readRequest = ArrayPrototypeShift(reader[_readRequests]); - readableByteStreamControllerFillReadRequestFromQueue( - controller, - readRequest, - ); - } + if (stream[_state] === "closed") { + const emptyView = new ctor( + pullIntoDescriptor.buffer, + pullIntoDescriptor.byteOffset, + 0, + ); + readIntoRequest.closeSteps(emptyView); + return; } - - /** - * @param {ReadableByteStreamController} controller - * @param {ArrayBufferView} view - * @param {ReadIntoRequest} readIntoRequest - * @returns {void} - */ - function readableByteStreamControllerPullInto( - controller, - view, - readIntoRequest, - ) { - const stream = controller[_stream]; - let elementSize = 1; - let ctor = DataView; - + if (controller[_queueTotalSize] > 0) { if ( - ObjectPrototypeIsPrototypeOf(Int8ArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(Uint8ClampedArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(Int16ArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(Uint16ArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(Int32ArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(Uint32ArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(BigInt64ArrayPrototype, view) || - ObjectPrototypeIsPrototypeOf(BigUint64ArrayPrototype, view) + readableByteStreamControllerFillPullIntoDescriptorFromQueue( + controller, + pullIntoDescriptor, + ) ) { - elementSize = view.constructor.BYTES_PER_ELEMENT; - ctor = view.constructor; - } - const byteOffset = view.byteOffset; - const byteLength = view.byteLength; - - /** @type {ArrayBufferLike} */ - let buffer; - - try { - buffer = transferArrayBuffer(view.buffer); - } catch (e) { - readIntoRequest.errorSteps(e); - return; - } - - /** @type {PullIntoDescriptor} */ - const pullIntoDescriptor = { - buffer, - bufferByteLength: buffer.byteLength, - byteOffset, - byteLength, - bytesFilled: 0, - elementSize, - viewConstructor: ctor, - readerType: "byob", - }; - - if (controller[_pendingPullIntos].length !== 0) { - ArrayPrototypePush(controller[_pendingPullIntos], pullIntoDescriptor); - readableStreamAddReadIntoRequest(stream, readIntoRequest); + const filledView = readableByteStreamControllerConvertPullIntoDescriptor( + pullIntoDescriptor, + ); + readableByteStreamControllerHandleQueueDrain(controller); + readIntoRequest.chunkSteps(filledView); return; } - if (stream[_state] === "closed") { - const emptyView = new ctor( - pullIntoDescriptor.buffer, - pullIntoDescriptor.byteOffset, - 0, + if (controller[_closeRequested]) { + const e = new TypeError( + "Insufficient bytes to fill elements in the given buffer", ); - readIntoRequest.closeSteps(emptyView); + readableByteStreamControllerError(controller, e); + readIntoRequest.errorSteps(e); return; } - if (controller[_queueTotalSize] > 0) { - if ( - readableByteStreamControllerFillPullIntoDescriptorFromQueue( - controller, - pullIntoDescriptor, - ) - ) { - const filledView = - readableByteStreamControllerConvertPullIntoDescriptor( - pullIntoDescriptor, - ); - readableByteStreamControllerHandleQueueDrain(controller); - readIntoRequest.chunkSteps(filledView); - return; - } - if (controller[_closeRequested]) { - const e = new TypeError( - "Insufficient bytes to fill elements in the given buffer", - ); - readableByteStreamControllerError(controller, e); - readIntoRequest.errorSteps(e); - return; - } - } - controller[_pendingPullIntos].push(pullIntoDescriptor); - readableStreamAddReadIntoRequest(stream, readIntoRequest); - readableByteStreamControllerCallPullIfNeeded(controller); } - - /** - * @param {ReadableByteStreamController} controller - * @param {number} bytesWritten - * @returns {void} - */ - function readableByteStreamControllerRespond(controller, bytesWritten) { - assert(controller[_pendingPullIntos].length !== 0); - const firstDescriptor = controller[_pendingPullIntos][0]; - const state = controller[_stream][_state]; - if (state === "closed") { - if (bytesWritten !== 0) { - throw new TypeError( - "bytesWritten must be 0 when calling respond() on a closed stream", - ); - } - } else { - assert(state === "readable"); - if (bytesWritten === 0) { - throw new TypeError( - "bytesWritten must be greater than 0 when calling respond() on a readable stream", - ); - } - if ( - (firstDescriptor.bytesFilled + bytesWritten) > - firstDescriptor.byteLength - ) { - throw new RangeError("bytesWritten out of range"); - } + controller[_pendingPullIntos].push(pullIntoDescriptor); + readableStreamAddReadIntoRequest(stream, readIntoRequest); + readableByteStreamControllerCallPullIfNeeded(controller); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {number} bytesWritten + * @returns {void} + */ +function readableByteStreamControllerRespond(controller, bytesWritten) { + assert(controller[_pendingPullIntos].length !== 0); + const firstDescriptor = controller[_pendingPullIntos][0]; + const state = controller[_stream][_state]; + if (state === "closed") { + if (bytesWritten !== 0) { + throw new TypeError( + "bytesWritten must be 0 when calling respond() on a closed stream", + ); } - firstDescriptor.buffer = transferArrayBuffer(firstDescriptor.buffer); - readableByteStreamControllerRespondInternal(controller, bytesWritten); - } - - /** - * @param {ReadableByteStreamController} controller - * @param {number} bytesWritten - * @param {PullIntoDescriptor} pullIntoDescriptor - * @returns {void} - */ - function readableByteStreamControllerRespondInReadableState( + } else { + assert(state === "readable"); + if (bytesWritten === 0) { + throw new TypeError( + "bytesWritten must be greater than 0 when calling respond() on a readable stream", + ); + } + if ( + (firstDescriptor.bytesFilled + bytesWritten) > + firstDescriptor.byteLength + ) { + throw new RangeError("bytesWritten out of range"); + } + } + firstDescriptor.buffer = transferArrayBuffer(firstDescriptor.buffer); + readableByteStreamControllerRespondInternal(controller, bytesWritten); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {number} bytesWritten + * @param {PullIntoDescriptor} pullIntoDescriptor + * @returns {void} + */ +function readableByteStreamControllerRespondInReadableState( + controller, + bytesWritten, + pullIntoDescriptor, +) { + assert( + (pullIntoDescriptor.bytesFilled + bytesWritten) <= + pullIntoDescriptor.byteLength, + ); + readableByteStreamControllerFillHeadPullIntoDescriptor( controller, bytesWritten, pullIntoDescriptor, - ) { - assert( - (pullIntoDescriptor.bytesFilled + bytesWritten) <= - pullIntoDescriptor.byteLength, - ); - readableByteStreamControllerFillHeadPullIntoDescriptor( + ); + if (pullIntoDescriptor.readerType === "none") { + readableByteStreamControllerEnqueueDetachedPullIntoToQueue( controller, - bytesWritten, - pullIntoDescriptor, - ); - if (pullIntoDescriptor.readerType === "none") { - readableByteStreamControllerEnqueueDetachedPullIntoToQueue( - controller, - pullIntoDescriptor, - ); - readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( - controller, - ); - return; - } - if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) { - return; - } - readableByteStreamControllerShiftPendingPullInto(controller); - const remainderSize = pullIntoDescriptor.bytesFilled % - pullIntoDescriptor.elementSize; - if (remainderSize > 0) { - const end = pullIntoDescriptor.byteOffset + - pullIntoDescriptor.bytesFilled; - readableByteStreamControllerEnqueueClonedChunkToQueue( - controller, - pullIntoDescriptor.buffer, - end - remainderSize, - remainderSize, - ); - } - pullIntoDescriptor.bytesFilled -= remainderSize; - readableByteStreamControllerCommitPullIntoDescriptor( - controller[_stream], pullIntoDescriptor, ); readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( controller, ); + return; + } + if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) { + return; + } + readableByteStreamControllerShiftPendingPullInto(controller); + const remainderSize = pullIntoDescriptor.bytesFilled % + pullIntoDescriptor.elementSize; + if (remainderSize > 0) { + const end = pullIntoDescriptor.byteOffset + + pullIntoDescriptor.bytesFilled; + readableByteStreamControllerEnqueueClonedChunkToQueue( + controller, + pullIntoDescriptor.buffer, + end - remainderSize, + remainderSize, + ); } - - /** - * @param {ReadableByteStreamController} controller - * @param {number} bytesWritten - * @returns {void} - */ - function readableByteStreamControllerRespondInternal( + pullIntoDescriptor.bytesFilled -= remainderSize; + readableByteStreamControllerCommitPullIntoDescriptor( + controller[_stream], + pullIntoDescriptor, + ); + readableByteStreamControllerProcessPullIntoDescriptorsUsingQueue( controller, - bytesWritten, - ) { - const firstDescriptor = controller[_pendingPullIntos][0]; - assert(canTransferArrayBuffer(firstDescriptor.buffer)); - readableByteStreamControllerInvalidateBYOBRequest(controller); - const state = controller[_stream][_state]; - if (state === "closed") { - assert(bytesWritten === 0); - readableByteStreamControllerRespondInClosedState( - controller, - firstDescriptor, - ); - } else { - assert(state === "readable"); - assert(bytesWritten > 0); - readableByteStreamControllerRespondInReadableState( - controller, - bytesWritten, - firstDescriptor, - ); - } - readableByteStreamControllerCallPullIfNeeded(controller); + ); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {number} bytesWritten + * @returns {void} + */ +function readableByteStreamControllerRespondInternal( + controller, + bytesWritten, +) { + const firstDescriptor = controller[_pendingPullIntos][0]; + assert(canTransferArrayBuffer(firstDescriptor.buffer)); + readableByteStreamControllerInvalidateBYOBRequest(controller); + const state = controller[_stream][_state]; + if (state === "closed") { + assert(bytesWritten === 0); + readableByteStreamControllerRespondInClosedState( + controller, + firstDescriptor, + ); + } else { + assert(state === "readable"); + assert(bytesWritten > 0); + readableByteStreamControllerRespondInReadableState( + controller, + bytesWritten, + firstDescriptor, + ); } - - /** - * @param {ReadableByteStreamController} controller - */ - function readableByteStreamControllerInvalidateBYOBRequest(controller) { - if (controller[_byobRequest] === null) { - return; - } - controller[_byobRequest][_controller] = undefined; - controller[_byobRequest][_view] = null; - controller[_byobRequest] = null; + readableByteStreamControllerCallPullIfNeeded(controller); +} + +/** + * @param {ReadableByteStreamController} controller + */ +function readableByteStreamControllerInvalidateBYOBRequest(controller) { + if (controller[_byobRequest] === null) { + return; + } + controller[_byobRequest][_controller] = undefined; + controller[_byobRequest][_view] = null; + controller[_byobRequest] = null; +} + +/** + * @param {ReadableByteStreamController} controller + * @param {PullIntoDescriptor} firstDescriptor + */ +function readableByteStreamControllerRespondInClosedState( + controller, + firstDescriptor, +) { + assert(firstDescriptor.bytesFilled === 0); + if (firstDescriptor.readerType === "none") { + readableByteStreamControllerShiftPendingPullInto(controller); } - - /** - * @param {ReadableByteStreamController} controller - * @param {PullIntoDescriptor} firstDescriptor - */ - function readableByteStreamControllerRespondInClosedState( - controller, - firstDescriptor, - ) { - assert(firstDescriptor.bytesFilled === 0); - if (firstDescriptor.readerType === "none") { - readableByteStreamControllerShiftPendingPullInto(controller); - } - const stream = controller[_stream]; - if (readableStreamHasBYOBReader(stream)) { - while (readableStreamGetNumReadIntoRequests(stream) > 0) { - const pullIntoDescriptor = - readableByteStreamControllerShiftPendingPullInto(controller); - readableByteStreamControllerCommitPullIntoDescriptor( - stream, - pullIntoDescriptor, - ); - } + const stream = controller[_stream]; + if (readableStreamHasBYOBReader(stream)) { + while (readableStreamGetNumReadIntoRequests(stream) > 0) { + const pullIntoDescriptor = + readableByteStreamControllerShiftPendingPullInto(controller); + readableByteStreamControllerCommitPullIntoDescriptor( + stream, + pullIntoDescriptor, + ); } } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {PullIntoDescriptor} pullIntoDescriptor - */ - function readableByteStreamControllerCommitPullIntoDescriptor( - stream, +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {PullIntoDescriptor} pullIntoDescriptor + */ +function readableByteStreamControllerCommitPullIntoDescriptor( + stream, + pullIntoDescriptor, +) { + assert(stream[_state] !== "errored"); + assert(pullIntoDescriptor.readerType !== "none"); + let done = false; + if (stream[_state] === "closed") { + assert(pullIntoDescriptor.bytesFilled === 0); + done = true; + } + const filledView = readableByteStreamControllerConvertPullIntoDescriptor( pullIntoDescriptor, - ) { - assert(stream[_state] !== "errored"); - assert(pullIntoDescriptor.readerType !== "none"); - let done = false; - if (stream[_state] === "closed") { - assert(pullIntoDescriptor.bytesFilled === 0); - done = true; - } - const filledView = readableByteStreamControllerConvertPullIntoDescriptor( - pullIntoDescriptor, - ); - if (pullIntoDescriptor.readerType === "default") { - readableStreamFulfillReadRequest(stream, filledView, done); - } else { - assert(pullIntoDescriptor.readerType === "byob"); - readableStreamFulfillReadIntoRequest(stream, filledView, done); - } - } - - /** - * @param {ReadableByteStreamController} controller - * @param {ArrayBufferView} view - */ - function readableByteStreamControllerRespondWithNewView(controller, view) { - assert(controller[_pendingPullIntos].length !== 0); - assert(!isDetachedBuffer(view.buffer)); - const firstDescriptor = controller[_pendingPullIntos][0]; - const state = controller[_stream][_state]; - if (state === "closed") { - if (view.byteLength !== 0) { - throw new TypeError( - "The view's length must be 0 when calling respondWithNewView() on a closed stream", - ); - } - } else { - assert(state === "readable"); - if (view.byteLength === 0) { - throw new TypeError( - "The view's length must be greater than 0 when calling respondWithNewView() on a readable stream", - ); - } - } - if ( - (firstDescriptor.byteOffset + firstDescriptor.bytesFilled) !== - view.byteOffset - ) { - throw new RangeError( - "The region specified by view does not match byobRequest", - ); - } - if (firstDescriptor.bufferByteLength !== view.buffer.byteLength) { - throw new RangeError( - "The buffer of view has different capacity than byobRequest", + ); + if (pullIntoDescriptor.readerType === "default") { + readableStreamFulfillReadRequest(stream, filledView, done); + } else { + assert(pullIntoDescriptor.readerType === "byob"); + readableStreamFulfillReadIntoRequest(stream, filledView, done); + } +} + +/** + * @param {ReadableByteStreamController} controller + * @param {ArrayBufferView} view + */ +function readableByteStreamControllerRespondWithNewView(controller, view) { + assert(controller[_pendingPullIntos].length !== 0); + assert(!isDetachedBuffer(view.buffer)); + const firstDescriptor = controller[_pendingPullIntos][0]; + const state = controller[_stream][_state]; + if (state === "closed") { + if (view.byteLength !== 0) { + throw new TypeError( + "The view's length must be 0 when calling respondWithNewView() on a closed stream", ); } - if ( - (firstDescriptor.bytesFilled + view.byteLength) > - firstDescriptor.byteLength - ) { - throw new RangeError( - "The region specified by view is larger than byobRequest", + } else { + assert(state === "readable"); + if (view.byteLength === 0) { + throw new TypeError( + "The view's length must be greater than 0 when calling respondWithNewView() on a readable stream", ); } - const viewByteLength = view.byteLength; - firstDescriptor.buffer = transferArrayBuffer(view.buffer); - readableByteStreamControllerRespondInternal(controller, viewByteLength); - } - - /** - * @param {ReadableByteStreamController} controller - * @returns {PullIntoDescriptor} - */ - function readableByteStreamControllerShiftPendingPullInto(controller) { - assert(controller[_byobRequest] === null); - return ArrayPrototypeShift(controller[_pendingPullIntos]); } - - /** - * @param {ReadableByteStreamController} controller - * @param {PullIntoDescriptor} pullIntoDescriptor - * @returns {boolean} - */ - function readableByteStreamControllerFillPullIntoDescriptorFromQueue( - controller, - pullIntoDescriptor, + if ( + (firstDescriptor.byteOffset + firstDescriptor.bytesFilled) !== + view.byteOffset ) { - const elementSize = pullIntoDescriptor.elementSize; - const currentAlignedBytes = pullIntoDescriptor.bytesFilled - - (pullIntoDescriptor.bytesFilled % elementSize); - const maxBytesToCopy = MathMin( - controller[_queueTotalSize], - pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled, + throw new RangeError( + "The region specified by view does not match byobRequest", ); - const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy; - const maxAlignedBytes = maxBytesFilled - (maxBytesFilled % elementSize); - let totalBytesToCopyRemaining = maxBytesToCopy; - let ready = false; - if (maxAlignedBytes > currentAlignedBytes) { - totalBytesToCopyRemaining = maxAlignedBytes - - pullIntoDescriptor.bytesFilled; - ready = true; - } - const queue = controller[_queue]; - while (totalBytesToCopyRemaining > 0) { - const headOfQueue = queue[0]; - const bytesToCopy = MathMin( - totalBytesToCopyRemaining, - headOfQueue.byteLength, - ); - const destStart = pullIntoDescriptor.byteOffset + - pullIntoDescriptor.bytesFilled; - - const destBuffer = new Uint8Array( - pullIntoDescriptor.buffer, - destStart, - bytesToCopy, - ); - const srcBuffer = new Uint8Array( - headOfQueue.buffer, - headOfQueue.byteOffset, - bytesToCopy, - ); - destBuffer.set(srcBuffer); - - if (headOfQueue.byteLength === bytesToCopy) { - ArrayPrototypeShift(queue); - } else { - headOfQueue.byteOffset += bytesToCopy; - headOfQueue.byteLength -= bytesToCopy; - } - controller[_queueTotalSize] -= bytesToCopy; - readableByteStreamControllerFillHeadPullIntoDescriptor( - controller, - bytesToCopy, - pullIntoDescriptor, - ); - totalBytesToCopyRemaining -= bytesToCopy; - } - if (!ready) { - assert(controller[_queueTotalSize] === 0); - assert(pullIntoDescriptor.bytesFilled > 0); - assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize); - } - return ready; } - - /** - * @param {ReadableByteStreamController} controller - * @param {ReadRequest} readRequest - * @returns {void} - */ - function readableByteStreamControllerFillReadRequestFromQueue( - controller, - readRequest, - ) { - assert(controller[_queueTotalSize] > 0); - const entry = ArrayPrototypeShift(controller[_queue]); - controller[_queueTotalSize] -= entry.byteLength; - readableByteStreamControllerHandleQueueDrain(controller); - const view = new Uint8Array( - entry.buffer, - entry.byteOffset, - entry.byteLength, + if (firstDescriptor.bufferByteLength !== view.buffer.byteLength) { + throw new RangeError( + "The buffer of view has different capacity than byobRequest", ); - readRequest.chunkSteps(view); } - - /** - * @param {ReadableByteStreamController} controller - * @param {number} size - * @param {PullIntoDescriptor} pullIntoDescriptor - * @returns {void} - */ - function readableByteStreamControllerFillHeadPullIntoDescriptor( - controller, - size, - pullIntoDescriptor, + if ( + (firstDescriptor.bytesFilled + view.byteLength) > + firstDescriptor.byteLength ) { - assert( - controller[_pendingPullIntos].length === 0 || - controller[_pendingPullIntos][0] === pullIntoDescriptor, + throw new RangeError( + "The region specified by view is larger than byobRequest", ); - assert(controller[_byobRequest] === null); - pullIntoDescriptor.bytesFilled += size; } + const viewByteLength = view.byteLength; + firstDescriptor.buffer = transferArrayBuffer(view.buffer); + readableByteStreamControllerRespondInternal(controller, viewByteLength); +} + +/** + * @param {ReadableByteStreamController} controller + * @returns {PullIntoDescriptor} + */ +function readableByteStreamControllerShiftPendingPullInto(controller) { + assert(controller[_byobRequest] === null); + return ArrayPrototypeShift(controller[_pendingPullIntos]); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {PullIntoDescriptor} pullIntoDescriptor + * @returns {boolean} + */ +function readableByteStreamControllerFillPullIntoDescriptorFromQueue( + controller, + pullIntoDescriptor, +) { + const elementSize = pullIntoDescriptor.elementSize; + const currentAlignedBytes = pullIntoDescriptor.bytesFilled - + (pullIntoDescriptor.bytesFilled % elementSize); + const maxBytesToCopy = MathMin( + controller[_queueTotalSize], + pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled, + ); + const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy; + const maxAlignedBytes = maxBytesFilled - (maxBytesFilled % elementSize); + let totalBytesToCopyRemaining = maxBytesToCopy; + let ready = false; + if (maxAlignedBytes > currentAlignedBytes) { + totalBytesToCopyRemaining = maxAlignedBytes - + pullIntoDescriptor.bytesFilled; + ready = true; + } + const queue = controller[_queue]; + while (totalBytesToCopyRemaining > 0) { + const headOfQueue = queue[0]; + const bytesToCopy = MathMin( + totalBytesToCopyRemaining, + headOfQueue.byteLength, + ); + const destStart = pullIntoDescriptor.byteOffset + + pullIntoDescriptor.bytesFilled; - /** - * @param {PullIntoDescriptor} pullIntoDescriptor - * @returns {ArrayBufferView} - */ - function readableByteStreamControllerConvertPullIntoDescriptor( - pullIntoDescriptor, - ) { - const bytesFilled = pullIntoDescriptor.bytesFilled; - const elementSize = pullIntoDescriptor.elementSize; - assert(bytesFilled <= pullIntoDescriptor.byteLength); - assert((bytesFilled % elementSize) === 0); - const buffer = transferArrayBuffer(pullIntoDescriptor.buffer); - return new pullIntoDescriptor.viewConstructor( - buffer, - pullIntoDescriptor.byteOffset, - bytesFilled / elementSize, + const destBuffer = new Uint8Array( + pullIntoDescriptor.buffer, + destStart, + bytesToCopy, ); - } + const srcBuffer = new Uint8Array( + headOfQueue.buffer, + headOfQueue.byteOffset, + bytesToCopy, + ); + destBuffer.set(srcBuffer); - /** - * @template R - * @param {ReadableStreamDefaultReader<R>} reader - * @param {ReadRequest<R>} readRequest - * @returns {void} - */ - function readableStreamDefaultReaderRead(reader, readRequest) { - const stream = reader[_stream]; - assert(stream); - stream[_disturbed] = true; - if (stream[_state] === "closed") { - readRequest.closeSteps(); - } else if (stream[_state] === "errored") { - readRequest.errorSteps(stream[_storedError]); + if (headOfQueue.byteLength === bytesToCopy) { + ArrayPrototypeShift(queue); } else { - assert(stream[_state] === "readable"); - stream[_controller][_pullSteps](readRequest); + headOfQueue.byteOffset += bytesToCopy; + headOfQueue.byteLength -= bytesToCopy; } - } - - /** - * @template R - * @param {ReadableStreamDefaultReader<R>} reader - */ - function readableStreamDefaultReaderRelease(reader) { - readableStreamReaderGenericRelease(reader); - const e = new TypeError("The reader was released."); - readableStreamDefaultReaderErrorReadRequests(reader, e); - } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {any} e - */ - function readableStreamError(stream, e) { + controller[_queueTotalSize] -= bytesToCopy; + readableByteStreamControllerFillHeadPullIntoDescriptor( + controller, + bytesToCopy, + pullIntoDescriptor, + ); + totalBytesToCopyRemaining -= bytesToCopy; + } + if (!ready) { + assert(controller[_queueTotalSize] === 0); + assert(pullIntoDescriptor.bytesFilled > 0); + assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize); + } + return ready; +} + +/** + * @param {ReadableByteStreamController} controller + * @param {ReadRequest} readRequest + * @returns {void} + */ +function readableByteStreamControllerFillReadRequestFromQueue( + controller, + readRequest, +) { + assert(controller[_queueTotalSize] > 0); + const entry = ArrayPrototypeShift(controller[_queue]); + controller[_queueTotalSize] -= entry.byteLength; + readableByteStreamControllerHandleQueueDrain(controller); + const view = new Uint8Array( + entry.buffer, + entry.byteOffset, + entry.byteLength, + ); + readRequest.chunkSteps(view); +} + +/** + * @param {ReadableByteStreamController} controller + * @param {number} size + * @param {PullIntoDescriptor} pullIntoDescriptor + * @returns {void} + */ +function readableByteStreamControllerFillHeadPullIntoDescriptor( + controller, + size, + pullIntoDescriptor, +) { + assert( + controller[_pendingPullIntos].length === 0 || + controller[_pendingPullIntos][0] === pullIntoDescriptor, + ); + assert(controller[_byobRequest] === null); + pullIntoDescriptor.bytesFilled += size; +} + +/** + * @param {PullIntoDescriptor} pullIntoDescriptor + * @returns {ArrayBufferView} + */ +function readableByteStreamControllerConvertPullIntoDescriptor( + pullIntoDescriptor, +) { + const bytesFilled = pullIntoDescriptor.bytesFilled; + const elementSize = pullIntoDescriptor.elementSize; + assert(bytesFilled <= pullIntoDescriptor.byteLength); + assert((bytesFilled % elementSize) === 0); + const buffer = transferArrayBuffer(pullIntoDescriptor.buffer); + return new pullIntoDescriptor.viewConstructor( + buffer, + pullIntoDescriptor.byteOffset, + bytesFilled / elementSize, + ); +} + +/** + * @template R + * @param {ReadableStreamDefaultReader<R>} reader + * @param {ReadRequest<R>} readRequest + * @returns {void} + */ +function readableStreamDefaultReaderRead(reader, readRequest) { + const stream = reader[_stream]; + assert(stream); + stream[_disturbed] = true; + if (stream[_state] === "closed") { + readRequest.closeSteps(); + } else if (stream[_state] === "errored") { + readRequest.errorSteps(stream[_storedError]); + } else { assert(stream[_state] === "readable"); - stream[_state] = "errored"; - stream[_storedError] = e; - /** @type {ReadableStreamDefaultReader<R> | undefined} */ - const reader = stream[_reader]; - if (reader === undefined) { - return; - } - /** @type {Deferred<void>} */ - const closedPromise = reader[_closedPromise]; - closedPromise.reject(e); - setPromiseIsHandledToTrue(closedPromise.promise); - if (isReadableStreamDefaultReader(reader)) { - readableStreamDefaultReaderErrorReadRequests(reader, e); - } else { - assert(isReadableStreamBYOBReader(reader)); - readableStreamBYOBReaderErrorReadIntoRequests(reader, e); - } - } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {R} chunk - * @param {boolean} done - */ - function readableStreamFulfillReadIntoRequest(stream, chunk, done) { - assert(readableStreamHasBYOBReader(stream)); - /** @type {ReadableStreamDefaultReader<R>} */ - const reader = stream[_reader]; - assert(reader[_readIntoRequests].length !== 0); - /** @type {ReadIntoRequest} */ - const readIntoRequest = ArrayPrototypeShift(reader[_readIntoRequests]); - if (done) { - readIntoRequest.closeSteps(chunk); - } else { - readIntoRequest.chunkSteps(chunk); - } - } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {R} chunk - * @param {boolean} done - */ - function readableStreamFulfillReadRequest(stream, chunk, done) { - assert(readableStreamHasDefaultReader(stream) === true); - /** @type {ReadableStreamDefaultReader<R>} */ - const reader = stream[_reader]; - assert(reader[_readRequests].length); - /** @type {ReadRequest<R>} */ - const readRequest = ArrayPrototypeShift(reader[_readRequests]); - if (done) { - readRequest.closeSteps(); - } else { - readRequest.chunkSteps(chunk); - } + stream[_controller][_pullSteps](readRequest); + } +} + +/** + * @template R + * @param {ReadableStreamDefaultReader<R>} reader + */ +function readableStreamDefaultReaderRelease(reader) { + readableStreamReaderGenericRelease(reader); + const e = new TypeError("The reader was released."); + readableStreamDefaultReaderErrorReadRequests(reader, e); +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {any} e + */ +function readableStreamError(stream, e) { + assert(stream[_state] === "readable"); + stream[_state] = "errored"; + stream[_storedError] = e; + /** @type {ReadableStreamDefaultReader<R> | undefined} */ + const reader = stream[_reader]; + if (reader === undefined) { + return; + } + /** @type {Deferred<void>} */ + const closedPromise = reader[_closedPromise]; + closedPromise.reject(e); + setPromiseIsHandledToTrue(closedPromise.promise); + if (isReadableStreamDefaultReader(reader)) { + readableStreamDefaultReaderErrorReadRequests(reader, e); + } else { + assert(isReadableStreamBYOBReader(reader)); + readableStreamBYOBReaderErrorReadIntoRequests(reader, e); } - - /** - * @param {ReadableStream} stream - * @return {number} - */ - function readableStreamGetNumReadIntoRequests(stream) { - assert(readableStreamHasBYOBReader(stream) === true); - return stream[_reader][_readIntoRequests].length; +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {R} chunk + * @param {boolean} done + */ +function readableStreamFulfillReadIntoRequest(stream, chunk, done) { + assert(readableStreamHasBYOBReader(stream)); + /** @type {ReadableStreamDefaultReader<R>} */ + const reader = stream[_reader]; + assert(reader[_readIntoRequests].length !== 0); + /** @type {ReadIntoRequest} */ + const readIntoRequest = ArrayPrototypeShift(reader[_readIntoRequests]); + if (done) { + readIntoRequest.closeSteps(chunk); + } else { + readIntoRequest.chunkSteps(chunk); + } +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {R} chunk + * @param {boolean} done + */ +function readableStreamFulfillReadRequest(stream, chunk, done) { + assert(readableStreamHasDefaultReader(stream) === true); + /** @type {ReadableStreamDefaultReader<R>} */ + const reader = stream[_reader]; + assert(reader[_readRequests].length); + /** @type {ReadRequest<R>} */ + const readRequest = ArrayPrototypeShift(reader[_readRequests]); + if (done) { + readRequest.closeSteps(); + } else { + readRequest.chunkSteps(chunk); + } +} + +/** + * @param {ReadableStream} stream + * @return {number} + */ +function readableStreamGetNumReadIntoRequests(stream) { + assert(readableStreamHasBYOBReader(stream) === true); + return stream[_reader][_readIntoRequests].length; +} + +/** + * @param {ReadableStream} stream + * @return {number} + */ +function readableStreamGetNumReadRequests(stream) { + assert(readableStreamHasDefaultReader(stream) === true); + return stream[_reader][_readRequests].length; +} + +/** + * @param {ReadableStream} stream + * @returns {boolean} + */ +function readableStreamHasBYOBReader(stream) { + const reader = stream[_reader]; + if (reader === undefined) { + return false; } - - /** - * @param {ReadableStream} stream - * @return {number} - */ - function readableStreamGetNumReadRequests(stream) { - assert(readableStreamHasDefaultReader(stream) === true); - return stream[_reader][_readRequests].length; + if (isReadableStreamBYOBReader(reader)) { + return true; } + return false; +} - /** - * @param {ReadableStream} stream - * @returns {boolean} - */ - function readableStreamHasBYOBReader(stream) { - const reader = stream[_reader]; - if (reader === undefined) { - return false; - } - if (isReadableStreamBYOBReader(reader)) { - return true; - } +/** + * @param {ReadableStream} stream + * @returns {boolean} + */ +function readableStreamHasDefaultReader(stream) { + const reader = stream[_reader]; + if (reader === undefined) { return false; } - - /** - * @param {ReadableStream} stream - * @returns {boolean} - */ - function readableStreamHasDefaultReader(stream) { - const reader = stream[_reader]; - if (reader === undefined) { - return false; - } - if (isReadableStreamDefaultReader(reader)) { - return true; - } - return false; + if (isReadableStreamDefaultReader(reader)) { + return true; } - - /** - * @template T - * @param {ReadableStream<T>} source - * @param {WritableStream<T>} dest - * @param {boolean} preventClose - * @param {boolean} preventAbort - * @param {boolean} preventCancel - * @param {AbortSignal=} signal - * @returns {Promise<void>} - */ - function readableStreamPipeTo( - source, - dest, - preventClose, - preventAbort, - preventCancel, - signal, - ) { - assert(isReadableStream(source)); - assert(isWritableStream(dest)); - assert( - typeof preventClose === "boolean" && typeof preventAbort === "boolean" && - typeof preventCancel === "boolean", - ); - assert( - signal === undefined || - ObjectPrototypeIsPrototypeOf(AbortSignalPrototype, signal), - ); - assert(!isReadableStreamLocked(source)); - assert(!isWritableStreamLocked(dest)); - // We use acquireReadableStreamDefaultReader even in case of ReadableByteStreamController - // as the spec allows us, and the only reason to use BYOBReader is to do some smart things - // with it, but the spec does not specify what things, so to simplify we stick to DefaultReader. - const reader = acquireReadableStreamDefaultReader(source); - const writer = acquireWritableStreamDefaultWriter(dest); - source[_disturbed] = true; - let shuttingDown = false; - let currentWrite = resolvePromiseWith(undefined); - /** @type {Deferred<void>} */ - const promise = new Deferred(); - /** @type {() => void} */ - let abortAlgorithm; - if (signal) { - abortAlgorithm = () => { - const error = signal.reason; - /** @type {Array<() => Promise<void>>} */ - const actions = []; - if (preventAbort === false) { - ArrayPrototypePush(actions, () => { - if (dest[_state] === "writable") { - return writableStreamAbort(dest, error); - } else { - return resolvePromiseWith(undefined); - } - }); - } - if (preventCancel === false) { - ArrayPrototypePush(actions, () => { - if (source[_state] === "readable") { - return readableStreamCancel(source, error); - } else { - return resolvePromiseWith(undefined); - } - }); - } - shutdownWithAction( - () => - SafePromiseAll(ArrayPrototypeMap(actions, (action) => action())), - true, - error, - ); - }; - - if (signal.aborted) { - abortAlgorithm(); - return promise.promise; + return false; +} + +/** + * @template T + * @param {ReadableStream<T>} source + * @param {WritableStream<T>} dest + * @param {boolean} preventClose + * @param {boolean} preventAbort + * @param {boolean} preventCancel + * @param {AbortSignal=} signal + * @returns {Promise<void>} + */ +function readableStreamPipeTo( + source, + dest, + preventClose, + preventAbort, + preventCancel, + signal, +) { + assert(isReadableStream(source)); + assert(isWritableStream(dest)); + assert( + typeof preventClose === "boolean" && typeof preventAbort === "boolean" && + typeof preventCancel === "boolean", + ); + assert( + signal === undefined || + ObjectPrototypeIsPrototypeOf(AbortSignalPrototype, signal), + ); + assert(!isReadableStreamLocked(source)); + assert(!isWritableStreamLocked(dest)); + // We use acquireReadableStreamDefaultReader even in case of ReadableByteStreamController + // as the spec allows us, and the only reason to use BYOBReader is to do some smart things + // with it, but the spec does not specify what things, so to simplify we stick to DefaultReader. + const reader = acquireReadableStreamDefaultReader(source); + const writer = acquireWritableStreamDefaultWriter(dest); + source[_disturbed] = true; + let shuttingDown = false; + let currentWrite = resolvePromiseWith(undefined); + /** @type {Deferred<void>} */ + const promise = new Deferred(); + /** @type {() => void} */ + let abortAlgorithm; + if (signal) { + abortAlgorithm = () => { + const error = signal.reason; + /** @type {Array<() => Promise<void>>} */ + const actions = []; + if (preventAbort === false) { + ArrayPrototypePush(actions, () => { + if (dest[_state] === "writable") { + return writableStreamAbort(dest, error); + } else { + return resolvePromiseWith(undefined); + } + }); } - signal[add](abortAlgorithm); - } - - function pipeLoop() { - return new Promise((resolveLoop, rejectLoop) => { - /** @param {boolean} done */ - function next(done) { - if (done) { - resolveLoop(); + if (preventCancel === false) { + ArrayPrototypePush(actions, () => { + if (source[_state] === "readable") { + return readableStreamCancel(source, error); } else { - uponPromise(pipeStep(), next, rejectLoop); + return resolvePromiseWith(undefined); } - } - next(false); - }); - } - - /** @returns {Promise<boolean>} */ - function pipeStep() { - if (shuttingDown === true) { - return resolvePromiseWith(true); + }); } + shutdownWithAction( + () => SafePromiseAll(ArrayPrototypeMap(actions, (action) => action())), + true, + error, + ); + }; - return transformPromiseWith(writer[_readyPromise].promise, () => { - return new Promise((resolveRead, rejectRead) => { - readableStreamDefaultReaderRead( - reader, - { - chunkSteps(chunk) { - currentWrite = transformPromiseWith( - writableStreamDefaultWriterWrite(writer, chunk), - undefined, - () => {}, - ); - resolveRead(false); - }, - closeSteps() { - resolveRead(true); - }, - errorSteps: rejectRead, - }, - ); - }); - }); + if (signal.aborted) { + abortAlgorithm(); + return promise.promise; } + signal[add](abortAlgorithm); + } - isOrBecomesErrored( - source, - reader[_closedPromise].promise, - (storedError) => { - if (preventAbort === false) { - shutdownWithAction( - () => writableStreamAbort(dest, storedError), - true, - storedError, - ); + function pipeLoop() { + return new Promise((resolveLoop, rejectLoop) => { + /** @param {boolean} done */ + function next(done) { + if (done) { + resolveLoop(); } else { - shutdown(true, storedError); + uponPromise(pipeStep(), next, rejectLoop); } - }, - ); - - isOrBecomesErrored(dest, writer[_closedPromise].promise, (storedError) => { - if (preventCancel === false) { - shutdownWithAction( - () => readableStreamCancel(source, storedError), - true, - storedError, - ); - } else { - shutdown(true, storedError); } + next(false); }); + } + + /** @returns {Promise<boolean>} */ + function pipeStep() { + if (shuttingDown === true) { + return resolvePromiseWith(true); + } - isOrBecomesClosed(source, reader[_closedPromise].promise, () => { - if (preventClose === false) { - shutdownWithAction(() => - writableStreamDefaultWriterCloseWithErrorPropagation(writer) + return transformPromiseWith(writer[_readyPromise].promise, () => { + return new Promise((resolveRead, rejectRead) => { + readableStreamDefaultReaderRead( + reader, + { + chunkSteps(chunk) { + currentWrite = transformPromiseWith( + writableStreamDefaultWriterWrite(writer, chunk), + undefined, + () => {}, + ); + resolveRead(false); + }, + closeSteps() { + resolveRead(true); + }, + errorSteps: rejectRead, + }, ); - } else { - shutdown(); - } + }); }); + } - if ( - writableStreamCloseQueuedOrInFlight(dest) === true || - dest[_state] === "closed" - ) { - const destClosed = new TypeError( - "The destination writable stream closed before all the data could be piped to it.", - ); - if (preventCancel === false) { + isOrBecomesErrored( + source, + reader[_closedPromise].promise, + (storedError) => { + if (preventAbort === false) { shutdownWithAction( - () => readableStreamCancel(source, destClosed), + () => writableStreamAbort(dest, storedError), true, - destClosed, + storedError, ); } else { - shutdown(true, destClosed); + shutdown(true, storedError); } - } - - setPromiseIsHandledToTrue(pipeLoop()); - - return promise.promise; + }, + ); - /** @returns {Promise<void>} */ - function waitForWritesToFinish() { - const oldCurrentWrite = currentWrite; - return transformPromiseWith( - currentWrite, - () => - oldCurrentWrite !== currentWrite - ? waitForWritesToFinish() - : undefined, + isOrBecomesErrored(dest, writer[_closedPromise].promise, (storedError) => { + if (preventCancel === false) { + shutdownWithAction( + () => readableStreamCancel(source, storedError), + true, + storedError, ); + } else { + shutdown(true, storedError); } + }); - /** - * @param {ReadableStream | WritableStream} stream - * @param {Promise<any>} promise - * @param {(e: any) => void} action - */ - function isOrBecomesErrored(stream, promise, action) { - if (stream[_state] === "errored") { - action(stream[_storedError]); - } else { - uponRejection(promise, action); - } - } - - /** - * @param {ReadableStream} stream - * @param {Promise<any>} promise - * @param {() => void} action - */ - function isOrBecomesClosed(stream, promise, action) { - if (stream[_state] === "closed") { - action(); - } else { - uponFulfillment(promise, action); - } + isOrBecomesClosed(source, reader[_closedPromise].promise, () => { + if (preventClose === false) { + shutdownWithAction(() => + writableStreamDefaultWriterCloseWithErrorPropagation(writer) + ); + } else { + shutdown(); } + }); - /** - * @param {() => Promise<void[] | void>} action - * @param {boolean=} originalIsError - * @param {any=} originalError - */ - function shutdownWithAction(action, originalIsError, originalError) { - function doTheRest() { - uponPromise( - action(), - () => finalize(originalIsError, originalError), - (newError) => finalize(true, newError), - ); - } - - if (shuttingDown === true) { - return; - } - shuttingDown = true; - - if ( - dest[_state] === "writable" && - writableStreamCloseQueuedOrInFlight(dest) === false - ) { - uponFulfillment(waitForWritesToFinish(), doTheRest); - } else { - doTheRest(); - } + if ( + writableStreamCloseQueuedOrInFlight(dest) === true || + dest[_state] === "closed" + ) { + const destClosed = new TypeError( + "The destination writable stream closed before all the data could be piped to it.", + ); + if (preventCancel === false) { + shutdownWithAction( + () => readableStreamCancel(source, destClosed), + true, + destClosed, + ); + } else { + shutdown(true, destClosed); } + } - /** - * @param {boolean=} isError - * @param {any=} error - */ - function shutdown(isError, error) { - if (shuttingDown) { - return; - } - shuttingDown = true; - if ( - dest[_state] === "writable" && - writableStreamCloseQueuedOrInFlight(dest) === false - ) { - uponFulfillment( - waitForWritesToFinish(), - () => finalize(isError, error), - ); - } else { - finalize(isError, error); - } - } + setPromiseIsHandledToTrue(pipeLoop()); - /** - * @param {boolean=} isError - * @param {any=} error - */ - function finalize(isError, error) { - writableStreamDefaultWriterRelease(writer); - readableStreamDefaultReaderRelease(reader); + return promise.promise; - if (signal !== undefined) { - signal[remove](abortAlgorithm); - } - if (isError) { - promise.reject(error); - } else { - promise.resolve(undefined); - } - } + /** @returns {Promise<void>} */ + function waitForWritesToFinish() { + const oldCurrentWrite = currentWrite; + return transformPromiseWith( + currentWrite, + () => + oldCurrentWrite !== currentWrite ? waitForWritesToFinish() : undefined, + ); } /** - * @param {ReadableStreamGenericReader<any> | ReadableStreamBYOBReader} reader - * @param {any} reason - * @returns {Promise<void>} + * @param {ReadableStream | WritableStream} stream + * @param {Promise<any>} promise + * @param {(e: any) => void} action */ - function readableStreamReaderGenericCancel(reader, reason) { - const stream = reader[_stream]; - assert(stream !== undefined); - return readableStreamCancel(stream, reason); + function isOrBecomesErrored(stream, promise, action) { + if (stream[_state] === "errored") { + action(stream[_storedError]); + } else { + uponRejection(promise, action); + } } /** - * @template R - * @param {ReadableStreamDefaultReader<R> | ReadableStreamBYOBReader} reader - * @param {ReadableStream<R>} stream + * @param {ReadableStream} stream + * @param {Promise<any>} promise + * @param {() => void} action */ - function readableStreamReaderGenericInitialize(reader, stream) { - reader[_stream] = stream; - stream[_reader] = reader; - if (stream[_state] === "readable") { - reader[_closedPromise] = new Deferred(); - } else if (stream[_state] === "closed") { - reader[_closedPromise] = new Deferred(); - reader[_closedPromise].resolve(undefined); + function isOrBecomesClosed(stream, promise, action) { + if (stream[_state] === "closed") { + action(); } else { - assert(stream[_state] === "errored"); - reader[_closedPromise] = new Deferred(); - reader[_closedPromise].reject(stream[_storedError]); - setPromiseIsHandledToTrue(reader[_closedPromise].promise); + uponFulfillment(promise, action); } } /** - * @template R - * @param {ReadableStreamGenericReader<R> | ReadableStreamBYOBReader} reader + * @param {() => Promise<void[] | void>} action + * @param {boolean=} originalIsError + * @param {any=} originalError */ - function readableStreamReaderGenericRelease(reader) { - const stream = reader[_stream]; - assert(stream !== undefined); - assert(stream[_reader] === reader); - if (stream[_state] === "readable") { - reader[_closedPromise].reject( - new TypeError( - "Reader was released and can no longer be used to monitor the stream's closedness.", - ), - ); - } else { - reader[_closedPromise] = new Deferred(); - reader[_closedPromise].reject( - new TypeError( - "Reader was released and can no longer be used to monitor the stream's closedness.", - ), + function shutdownWithAction(action, originalIsError, originalError) { + function doTheRest() { + uponPromise( + action(), + () => finalize(originalIsError, originalError), + (newError) => finalize(true, newError), ); } - setPromiseIsHandledToTrue(reader[_closedPromise].promise); - stream[_controller][_releaseSteps](); - stream[_reader] = undefined; - reader[_stream] = undefined; - } - /** - * @param {ReadableStreamBYOBReader} reader - * @param {any} e - */ - function readableStreamBYOBReaderErrorReadIntoRequests(reader, e) { - const readIntoRequests = reader[_readIntoRequests]; - reader[_readIntoRequests] = []; - for (let i = 0; i < readIntoRequests.length; ++i) { - const readIntoRequest = readIntoRequests[i]; - readIntoRequest.errorSteps(e); + if (shuttingDown === true) { + return; + } + shuttingDown = true; + + if ( + dest[_state] === "writable" && + writableStreamCloseQueuedOrInFlight(dest) === false + ) { + uponFulfillment(waitForWritesToFinish(), doTheRest); + } else { + doTheRest(); } } /** - * @template R - * @param {ReadableStream<R>} stream - * @param {boolean} cloneForBranch2 - * @returns {[ReadableStream<R>, ReadableStream<R>]} + * @param {boolean=} isError + * @param {any=} error */ - function readableStreamTee(stream, cloneForBranch2) { - assert(isReadableStream(stream)); - assert(typeof cloneForBranch2 === "boolean"); + function shutdown(isError, error) { + if (shuttingDown) { + return; + } + shuttingDown = true; if ( - ObjectPrototypeIsPrototypeOf( - ReadableByteStreamControllerPrototype, - stream[_controller], - ) + dest[_state] === "writable" && + writableStreamCloseQueuedOrInFlight(dest) === false ) { - return readableByteStreamTee(stream); + uponFulfillment( + waitForWritesToFinish(), + () => finalize(isError, error), + ); } else { - return readableStreamDefaultTee(stream, cloneForBranch2); + finalize(isError, error); } } /** - * @template R - * @param {ReadableStream<R>} stream - * @param {boolean} cloneForBranch2 - * @returns {[ReadableStream<R>, ReadableStream<R>]} + * @param {boolean=} isError + * @param {any=} error */ - function readableStreamDefaultTee(stream, cloneForBranch2) { - assert(isReadableStream(stream)); - assert(typeof cloneForBranch2 === "boolean"); - const reader = acquireReadableStreamDefaultReader(stream); - let reading = false; - let readAgain = false; - let canceled1 = false; - let canceled2 = false; - /** @type {any} */ - let reason1; - /** @type {any} */ - let reason2; - /** @type {ReadableStream<R>} */ - // deno-lint-ignore prefer-const - let branch1; - /** @type {ReadableStream<R>} */ - // deno-lint-ignore prefer-const - let branch2; + function finalize(isError, error) { + writableStreamDefaultWriterRelease(writer); + readableStreamDefaultReaderRelease(reader); - /** @type {Deferred<void>} */ - const cancelPromise = new Deferred(); + if (signal !== undefined) { + signal[remove](abortAlgorithm); + } + if (isError) { + promise.reject(error); + } else { + promise.resolve(undefined); + } + } +} + +/** + * @param {ReadableStreamGenericReader<any> | ReadableStreamBYOBReader} reader + * @param {any} reason + * @returns {Promise<void>} + */ +function readableStreamReaderGenericCancel(reader, reason) { + const stream = reader[_stream]; + assert(stream !== undefined); + return readableStreamCancel(stream, reason); +} + +/** + * @template R + * @param {ReadableStreamDefaultReader<R> | ReadableStreamBYOBReader} reader + * @param {ReadableStream<R>} stream + */ +function readableStreamReaderGenericInitialize(reader, stream) { + reader[_stream] = stream; + stream[_reader] = reader; + if (stream[_state] === "readable") { + reader[_closedPromise] = new Deferred(); + } else if (stream[_state] === "closed") { + reader[_closedPromise] = new Deferred(); + reader[_closedPromise].resolve(undefined); + } else { + assert(stream[_state] === "errored"); + reader[_closedPromise] = new Deferred(); + reader[_closedPromise].reject(stream[_storedError]); + setPromiseIsHandledToTrue(reader[_closedPromise].promise); + } +} + +/** + * @template R + * @param {ReadableStreamGenericReader<R> | ReadableStreamBYOBReader} reader + */ +function readableStreamReaderGenericRelease(reader) { + const stream = reader[_stream]; + assert(stream !== undefined); + assert(stream[_reader] === reader); + if (stream[_state] === "readable") { + reader[_closedPromise].reject( + new TypeError( + "Reader was released and can no longer be used to monitor the stream's closedness.", + ), + ); + } else { + reader[_closedPromise] = new Deferred(); + reader[_closedPromise].reject( + new TypeError( + "Reader was released and can no longer be used to monitor the stream's closedness.", + ), + ); + } + setPromiseIsHandledToTrue(reader[_closedPromise].promise); + stream[_controller][_releaseSteps](); + stream[_reader] = undefined; + reader[_stream] = undefined; +} + +/** + * @param {ReadableStreamBYOBReader} reader + * @param {any} e + */ +function readableStreamBYOBReaderErrorReadIntoRequests(reader, e) { + const readIntoRequests = reader[_readIntoRequests]; + reader[_readIntoRequests] = []; + for (let i = 0; i < readIntoRequests.length; ++i) { + const readIntoRequest = readIntoRequests[i]; + readIntoRequest.errorSteps(e); + } +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {boolean} cloneForBranch2 + * @returns {[ReadableStream<R>, ReadableStream<R>]} + */ +function readableStreamTee(stream, cloneForBranch2) { + assert(isReadableStream(stream)); + assert(typeof cloneForBranch2 === "boolean"); + if ( + ObjectPrototypeIsPrototypeOf( + ReadableByteStreamControllerPrototype, + stream[_controller], + ) + ) { + return readableByteStreamTee(stream); + } else { + return readableStreamDefaultTee(stream, cloneForBranch2); + } +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {boolean} cloneForBranch2 + * @returns {[ReadableStream<R>, ReadableStream<R>]} + */ +function readableStreamDefaultTee(stream, cloneForBranch2) { + assert(isReadableStream(stream)); + assert(typeof cloneForBranch2 === "boolean"); + const reader = acquireReadableStreamDefaultReader(stream); + let reading = false; + let readAgain = false; + let canceled1 = false; + let canceled2 = false; + /** @type {any} */ + let reason1; + /** @type {any} */ + let reason2; + /** @type {ReadableStream<R>} */ + // deno-lint-ignore prefer-const + let branch1; + /** @type {ReadableStream<R>} */ + // deno-lint-ignore prefer-const + let branch2; + + /** @type {Deferred<void>} */ + const cancelPromise = new Deferred(); + + function pullAlgorithm() { + if (reading === true) { + readAgain = true; + return resolvePromiseWith(undefined); + } + reading = true; + /** @type {ReadRequest<R>} */ + const readRequest = { + chunkSteps(value) { + queueMicrotask(() => { + readAgain = false; + const value1 = value; + const value2 = value; - function pullAlgorithm() { - if (reading === true) { - readAgain = true; - return resolvePromiseWith(undefined); - } - reading = true; - /** @type {ReadRequest<R>} */ - const readRequest = { - chunkSteps(value) { - queueMicrotask(() => { - readAgain = false; - const value1 = value; - const value2 = value; - - // TODO(lucacasonato): respect clonedForBranch2. - - if (canceled1 === false) { - readableStreamDefaultControllerEnqueue( - /** @type {ReadableStreamDefaultController<any>} */ branch1[ - _controller - ], - value1, - ); - } - if (canceled2 === false) { - readableStreamDefaultControllerEnqueue( - /** @type {ReadableStreamDefaultController<any>} */ branch2[ - _controller - ], - value2, - ); - } + // TODO(lucacasonato): respect clonedForBranch2. - reading = false; - if (readAgain === true) { - pullAlgorithm(); - } - }); - }, - closeSteps() { - reading = false; if (canceled1 === false) { - readableStreamDefaultControllerClose( + readableStreamDefaultControllerEnqueue( /** @type {ReadableStreamDefaultController<any>} */ branch1[ _controller ], + value1, ); } if (canceled2 === false) { - readableStreamDefaultControllerClose( + readableStreamDefaultControllerEnqueue( /** @type {ReadableStreamDefaultController<any>} */ branch2[ _controller ], + value2, ); } - if (canceled1 === false || canceled2 === false) { - cancelPromise.resolve(undefined); - } - }, - errorSteps() { + reading = false; - }, - }; - readableStreamDefaultReaderRead(reader, readRequest); - return resolvePromiseWith(undefined); + if (readAgain === true) { + pullAlgorithm(); + } + }); + }, + closeSteps() { + reading = false; + if (canceled1 === false) { + readableStreamDefaultControllerClose( + /** @type {ReadableStreamDefaultController<any>} */ branch1[ + _controller + ], + ); + } + if (canceled2 === false) { + readableStreamDefaultControllerClose( + /** @type {ReadableStreamDefaultController<any>} */ branch2[ + _controller + ], + ); + } + if (canceled1 === false || canceled2 === false) { + cancelPromise.resolve(undefined); + } + }, + errorSteps() { + reading = false; + }, + }; + readableStreamDefaultReaderRead(reader, readRequest); + return resolvePromiseWith(undefined); + } + + /** + * @param {any} reason + * @returns {Promise<void>} + */ + function cancel1Algorithm(reason) { + canceled1 = true; + reason1 = reason; + if (canceled2 === true) { + const compositeReason = [reason1, reason2]; + const cancelResult = readableStreamCancel(stream, compositeReason); + cancelPromise.resolve(cancelResult); } + return cancelPromise.promise; + } - /** - * @param {any} reason - * @returns {Promise<void>} - */ - function cancel1Algorithm(reason) { - canceled1 = true; - reason1 = reason; - if (canceled2 === true) { - const compositeReason = [reason1, reason2]; - const cancelResult = readableStreamCancel(stream, compositeReason); - cancelPromise.resolve(cancelResult); - } - return cancelPromise.promise; - } - - /** - * @param {any} reason - * @returns {Promise<void>} - */ - function cancel2Algorithm(reason) { - canceled2 = true; - reason2 = reason; - if (canceled1 === true) { - const compositeReason = [reason1, reason2]; - const cancelResult = readableStreamCancel(stream, compositeReason); - cancelPromise.resolve(cancelResult); - } - return cancelPromise.promise; + /** + * @param {any} reason + * @returns {Promise<void>} + */ + function cancel2Algorithm(reason) { + canceled2 = true; + reason2 = reason; + if (canceled1 === true) { + const compositeReason = [reason1, reason2]; + const cancelResult = readableStreamCancel(stream, compositeReason); + cancelPromise.resolve(cancelResult); } + return cancelPromise.promise; + } - function startAlgorithm() {} + function startAlgorithm() {} - branch1 = createReadableStream( - startAlgorithm, - pullAlgorithm, - cancel1Algorithm, + branch1 = createReadableStream( + startAlgorithm, + pullAlgorithm, + cancel1Algorithm, + ); + branch2 = createReadableStream( + startAlgorithm, + pullAlgorithm, + cancel2Algorithm, + ); + + uponRejection(reader[_closedPromise].promise, (r) => { + readableStreamDefaultControllerError( + /** @type {ReadableStreamDefaultController<any>} */ branch1[ + _controller + ], + r, ); - branch2 = createReadableStream( - startAlgorithm, - pullAlgorithm, - cancel2Algorithm, + readableStreamDefaultControllerError( + /** @type {ReadableStreamDefaultController<any>} */ branch2[ + _controller + ], + r, ); + if (canceled1 === false || canceled2 === false) { + cancelPromise.resolve(undefined); + } + }); - uponRejection(reader[_closedPromise].promise, (r) => { - readableStreamDefaultControllerError( - /** @type {ReadableStreamDefaultController<any>} */ branch1[ - _controller - ], - r, - ); - readableStreamDefaultControllerError( - /** @type {ReadableStreamDefaultController<any>} */ branch2[ - _controller - ], - r, - ); - if (canceled1 === false || canceled2 === false) { + return [branch1, branch2]; +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @returns {[ReadableStream<R>, ReadableStream<R>]} + */ +function readableByteStreamTee(stream) { + assert(isReadableStream(stream)); + assert( + ObjectPrototypeIsPrototypeOf( + ReadableByteStreamControllerPrototype, + stream[_controller], + ), + ); + let reader = acquireReadableStreamDefaultReader(stream); + let reading = false; + let readAgainForBranch1 = false; + let readAgainForBranch2 = false; + let canceled1 = false; + let canceled2 = false; + let reason1 = undefined; + let reason2 = undefined; + let branch1 = undefined; + let branch2 = undefined; + /** @type {Deferred<void>} */ + const cancelPromise = new Deferred(); + + /** + * @param {ReadableStreamBYOBReader} thisReader + */ + function forwardReaderError(thisReader) { + PromisePrototypeCatch(thisReader[_closedPromise].promise, (e) => { + if (thisReader !== reader) { + return; + } + readableByteStreamControllerError(branch1[_controller], e); + readableByteStreamControllerError(branch2[_controller], e); + if (!canceled1 || !canceled2) { cancelPromise.resolve(undefined); } }); - - return [branch1, branch2]; } - /** - * @template R - * @param {ReadableStream<R>} stream - * @returns {[ReadableStream<R>, ReadableStream<R>]} - */ - function readableByteStreamTee(stream) { - assert(isReadableStream(stream)); - assert( - ObjectPrototypeIsPrototypeOf( - ReadableByteStreamControllerPrototype, - stream[_controller], - ), - ); - let reader = acquireReadableStreamDefaultReader(stream); - let reading = false; - let readAgainForBranch1 = false; - let readAgainForBranch2 = false; - let canceled1 = false; - let canceled2 = false; - let reason1 = undefined; - let reason2 = undefined; - let branch1 = undefined; - let branch2 = undefined; - /** @type {Deferred<void>} */ - const cancelPromise = new Deferred(); - - /** - * @param {ReadableStreamBYOBReader} thisReader - */ - function forwardReaderError(thisReader) { - PromisePrototypeCatch(thisReader[_closedPromise].promise, (e) => { - if (thisReader !== reader) { - return; - } - readableByteStreamControllerError(branch1[_controller], e); - readableByteStreamControllerError(branch2[_controller], e); - if (!canceled1 || !canceled2) { - cancelPromise.resolve(undefined); - } - }); - } - - function pullWithDefaultReader() { - if (isReadableStreamBYOBReader(reader)) { - assert(reader[_readIntoRequests].length === 0); - readableStreamBYOBReaderRelease(reader); - reader = acquireReadableStreamDefaultReader(stream); - forwardReaderError(reader); - } - - /** @type {ReadRequest} */ - const readRequest = { - chunkSteps(chunk) { - queueMicrotask(() => { - readAgainForBranch1 = false; - readAgainForBranch2 = false; - const chunk1 = chunk; - let chunk2 = chunk; - if (!canceled1 && !canceled2) { - try { - chunk2 = cloneAsUint8Array(chunk); - } catch (e) { - readableByteStreamControllerError(branch1[_controller], e); - readableByteStreamControllerError(branch2[_controller], e); - cancelPromise.resolve(readableStreamCancel(stream, e)); - return; - } - } - if (!canceled1) { - readableByteStreamControllerEnqueue(branch1[_controller], chunk1); - } - if (!canceled2) { - readableByteStreamControllerEnqueue(branch2[_controller], chunk2); - } - reading = false; - if (readAgainForBranch1) { - pull1Algorithm(); - } else if (readAgainForBranch2) { - pull2Algorithm(); + function pullWithDefaultReader() { + if (isReadableStreamBYOBReader(reader)) { + assert(reader[_readIntoRequests].length === 0); + readableStreamBYOBReaderRelease(reader); + reader = acquireReadableStreamDefaultReader(stream); + forwardReaderError(reader); + } + + /** @type {ReadRequest} */ + const readRequest = { + chunkSteps(chunk) { + queueMicrotask(() => { + readAgainForBranch1 = false; + readAgainForBranch2 = false; + const chunk1 = chunk; + let chunk2 = chunk; + if (!canceled1 && !canceled2) { + try { + chunk2 = cloneAsUint8Array(chunk); + } catch (e) { + readableByteStreamControllerError(branch1[_controller], e); + readableByteStreamControllerError(branch2[_controller], e); + cancelPromise.resolve(readableStreamCancel(stream, e)); + return; } - }); - }, - closeSteps() { - reading = false; + } if (!canceled1) { - readableByteStreamControllerClose(branch1[_controller]); + readableByteStreamControllerEnqueue(branch1[_controller], chunk1); } if (!canceled2) { - readableByteStreamControllerClose(branch2[_controller]); - } - if (branch1[_controller][_pendingPullIntos].length !== 0) { - readableByteStreamControllerRespond(branch1[_controller], 0); + readableByteStreamControllerEnqueue(branch2[_controller], chunk2); } - if (branch2[_controller][_pendingPullIntos].length !== 0) { - readableByteStreamControllerRespond(branch2[_controller], 0); - } - if (!canceled1 || !canceled2) { - cancelPromise.resolve(undefined); - } - }, - errorSteps() { reading = false; - }, - }; - readableStreamDefaultReaderRead(reader, readRequest); - } + if (readAgainForBranch1) { + pull1Algorithm(); + } else if (readAgainForBranch2) { + pull2Algorithm(); + } + }); + }, + closeSteps() { + reading = false; + if (!canceled1) { + readableByteStreamControllerClose(branch1[_controller]); + } + if (!canceled2) { + readableByteStreamControllerClose(branch2[_controller]); + } + if (branch1[_controller][_pendingPullIntos].length !== 0) { + readableByteStreamControllerRespond(branch1[_controller], 0); + } + if (branch2[_controller][_pendingPullIntos].length !== 0) { + readableByteStreamControllerRespond(branch2[_controller], 0); + } + if (!canceled1 || !canceled2) { + cancelPromise.resolve(undefined); + } + }, + errorSteps() { + reading = false; + }, + }; + readableStreamDefaultReaderRead(reader, readRequest); + } - function pullWithBYOBReader(view, forBranch2) { - if (isReadableStreamDefaultReader(reader)) { - assert(reader[_readRequests].length === 0); - readableStreamDefaultReaderRelease(reader); - reader = acquireReadableStreamBYOBReader(stream); - forwardReaderError(reader); - } - const byobBranch = forBranch2 ? branch2 : branch1; - const otherBranch = forBranch2 ? branch1 : branch2; + function pullWithBYOBReader(view, forBranch2) { + if (isReadableStreamDefaultReader(reader)) { + assert(reader[_readRequests].length === 0); + readableStreamDefaultReaderRelease(reader); + reader = acquireReadableStreamBYOBReader(stream); + forwardReaderError(reader); + } + const byobBranch = forBranch2 ? branch2 : branch1; + const otherBranch = forBranch2 ? branch1 : branch2; - /** @type {ReadIntoRequest} */ - const readIntoRequest = { - chunkSteps(chunk) { - queueMicrotask(() => { - readAgainForBranch1 = false; - readAgainForBranch2 = false; - const byobCanceled = forBranch2 ? canceled2 : canceled1; - const otherCanceled = forBranch2 ? canceled1 : canceled2; - if (!otherCanceled) { - let clonedChunk; - try { - clonedChunk = cloneAsUint8Array(chunk); - } catch (e) { - readableByteStreamControllerError(byobBranch[_controller], e); - readableByteStreamControllerError(otherBranch[_controller], e); - cancelPromise.resolve(readableStreamCancel(stream, e)); - return; - } - if (!byobCanceled) { - readableByteStreamControllerRespondWithNewView( - byobBranch[_controller], - chunk, - ); - } - readableByteStreamControllerEnqueue( - otherBranch[_controller], - clonedChunk, - ); - } else if (!byobCanceled) { - readableByteStreamControllerRespondWithNewView( - byobBranch[_controller], - chunk, - ); - } - reading = false; - if (readAgainForBranch1) { - pull1Algorithm(); - } else if (readAgainForBranch2) { - pull2Algorithm(); - } - }); - }, - closeSteps(chunk) { - reading = false; + /** @type {ReadIntoRequest} */ + const readIntoRequest = { + chunkSteps(chunk) { + queueMicrotask(() => { + readAgainForBranch1 = false; + readAgainForBranch2 = false; const byobCanceled = forBranch2 ? canceled2 : canceled1; const otherCanceled = forBranch2 ? canceled1 : canceled2; - if (!byobCanceled) { - readableByteStreamControllerClose(byobBranch[_controller]); - } if (!otherCanceled) { - readableByteStreamControllerClose(otherBranch[_controller]); - } - if (chunk !== undefined) { - assert(chunk.byteLength === 0); + let clonedChunk; + try { + clonedChunk = cloneAsUint8Array(chunk); + } catch (e) { + readableByteStreamControllerError(byobBranch[_controller], e); + readableByteStreamControllerError(otherBranch[_controller], e); + cancelPromise.resolve(readableStreamCancel(stream, e)); + return; + } if (!byobCanceled) { readableByteStreamControllerRespondWithNewView( byobBranch[_controller], chunk, ); } - if ( - !otherCanceled && - otherBranch[_controller][_pendingPullIntos].length !== 0 - ) { - readableByteStreamControllerRespond(otherBranch[_controller], 0); - } - } - if (!byobCanceled || !otherCanceled) { - cancelPromise.resolve(undefined); + readableByteStreamControllerEnqueue( + otherBranch[_controller], + clonedChunk, + ); + } else if (!byobCanceled) { + readableByteStreamControllerRespondWithNewView( + byobBranch[_controller], + chunk, + ); } - }, - errorSteps() { reading = false; - }, - }; - readableStreamBYOBReaderRead(reader, view, readIntoRequest); - } + if (readAgainForBranch1) { + pull1Algorithm(); + } else if (readAgainForBranch2) { + pull2Algorithm(); + } + }); + }, + closeSteps(chunk) { + reading = false; + const byobCanceled = forBranch2 ? canceled2 : canceled1; + const otherCanceled = forBranch2 ? canceled1 : canceled2; + if (!byobCanceled) { + readableByteStreamControllerClose(byobBranch[_controller]); + } + if (!otherCanceled) { + readableByteStreamControllerClose(otherBranch[_controller]); + } + if (chunk !== undefined) { + assert(chunk.byteLength === 0); + if (!byobCanceled) { + readableByteStreamControllerRespondWithNewView( + byobBranch[_controller], + chunk, + ); + } + if ( + !otherCanceled && + otherBranch[_controller][_pendingPullIntos].length !== 0 + ) { + readableByteStreamControllerRespond(otherBranch[_controller], 0); + } + } + if (!byobCanceled || !otherCanceled) { + cancelPromise.resolve(undefined); + } + }, + errorSteps() { + reading = false; + }, + }; + readableStreamBYOBReaderRead(reader, view, readIntoRequest); + } - function pull1Algorithm() { - if (reading) { - readAgainForBranch1 = true; - return PromiseResolve(undefined); - } - reading = true; - const byobRequest = readableByteStreamControllerGetBYOBRequest( - branch1[_controller], - ); - if (byobRequest === null) { - pullWithDefaultReader(); - } else { - pullWithBYOBReader(byobRequest[_view], false); - } + function pull1Algorithm() { + if (reading) { + readAgainForBranch1 = true; return PromiseResolve(undefined); } + reading = true; + const byobRequest = readableByteStreamControllerGetBYOBRequest( + branch1[_controller], + ); + if (byobRequest === null) { + pullWithDefaultReader(); + } else { + pullWithBYOBReader(byobRequest[_view], false); + } + return PromiseResolve(undefined); + } - function pull2Algorithm() { - if (reading) { - readAgainForBranch2 = true; - return PromiseResolve(undefined); - } - reading = true; - const byobRequest = readableByteStreamControllerGetBYOBRequest( - branch2[_controller], - ); - if (byobRequest === null) { - pullWithDefaultReader(); - } else { - pullWithBYOBReader(byobRequest[_view], true); - } + function pull2Algorithm() { + if (reading) { + readAgainForBranch2 = true; return PromiseResolve(undefined); } - - function cancel1Algorithm(reason) { - canceled1 = true; - reason1 = reason; - if (canceled2) { - const compositeReason = [reason1, reason2]; - const cancelResult = readableStreamCancel(stream, compositeReason); - cancelPromise.resolve(cancelResult); - } - return cancelPromise.promise; + reading = true; + const byobRequest = readableByteStreamControllerGetBYOBRequest( + branch2[_controller], + ); + if (byobRequest === null) { + pullWithDefaultReader(); + } else { + pullWithBYOBReader(byobRequest[_view], true); } + return PromiseResolve(undefined); + } - function cancel2Algorithm(reason) { - canceled2 = true; - reason2 = reason; - if (canceled1) { - const compositeReason = [reason1, reason2]; - const cancelResult = readableStreamCancel(stream, compositeReason); - cancelPromise.resolve(cancelResult); - } - return cancelPromise.promise; + function cancel1Algorithm(reason) { + canceled1 = true; + reason1 = reason; + if (canceled2) { + const compositeReason = [reason1, reason2]; + const cancelResult = readableStreamCancel(stream, compositeReason); + cancelPromise.resolve(cancelResult); } + return cancelPromise.promise; + } - function startAlgorithm() { - return undefined; + function cancel2Algorithm(reason) { + canceled2 = true; + reason2 = reason; + if (canceled1) { + const compositeReason = [reason1, reason2]; + const cancelResult = readableStreamCancel(stream, compositeReason); + cancelPromise.resolve(cancelResult); } + return cancelPromise.promise; + } - branch1 = createReadableByteStream( - startAlgorithm, - pull1Algorithm, - cancel1Algorithm, - ); - branch2 = createReadableByteStream( - startAlgorithm, - pull2Algorithm, - cancel2Algorithm, - ); + function startAlgorithm() { + return undefined; + } - branch1[_original] = stream; - branch2[_original] = stream; + branch1 = createReadableByteStream( + startAlgorithm, + pull1Algorithm, + cancel1Algorithm, + ); + branch2 = createReadableByteStream( + startAlgorithm, + pull2Algorithm, + cancel2Algorithm, + ); - forwardReaderError(reader); - return [branch1, branch2]; + branch1[_original] = stream; + branch2[_original] = stream; + + forwardReaderError(reader); + return [branch1, branch2]; +} + +/** + * @param {ReadableStream<ArrayBuffer>} stream + * @param {ReadableByteStreamController} controller + * @param {() => void} startAlgorithm + * @param {() => Promise<void>} pullAlgorithm + * @param {(reason: any) => Promise<void>} cancelAlgorithm + * @param {number} highWaterMark + * @param {number | undefined} autoAllocateChunkSize + */ +function setUpReadableByteStreamController( + stream, + controller, + startAlgorithm, + pullAlgorithm, + cancelAlgorithm, + highWaterMark, + autoAllocateChunkSize, +) { + assert(stream[_controller] === undefined); + if (autoAllocateChunkSize !== undefined) { + assert(NumberIsInteger(autoAllocateChunkSize)); + assert(autoAllocateChunkSize >= 0); + } + controller[_stream] = stream; + controller[_pullAgain] = controller[_pulling] = false; + controller[_byobRequest] = null; + resetQueue(controller); + controller[_closeRequested] = controller[_started] = false; + controller[_strategyHWM] = highWaterMark; + controller[_pullAlgorithm] = pullAlgorithm; + controller[_cancelAlgorithm] = cancelAlgorithm; + controller[_autoAllocateChunkSize] = autoAllocateChunkSize; + controller[_pendingPullIntos] = []; + stream[_controller] = controller; + const startResult = startAlgorithm(); + const startPromise = resolvePromiseWith(startResult); + setPromiseIsHandledToTrue( + PromisePrototypeThen( + startPromise, + () => { + controller[_started] = true; + assert(controller[_pulling] === false); + assert(controller[_pullAgain] === false); + readableByteStreamControllerCallPullIfNeeded(controller); + }, + (r) => { + readableByteStreamControllerError(controller, r); + }, + ), + ); +} + +/** + * @param {ReadableStream<ArrayBuffer>} stream + * @param {UnderlyingSource<ArrayBuffer>} underlyingSource + * @param {UnderlyingSource<ArrayBuffer>} underlyingSourceDict + * @param {number} highWaterMark + */ +function setUpReadableByteStreamControllerFromUnderlyingSource( + stream, + underlyingSource, + underlyingSourceDict, + highWaterMark, +) { + const controller = webidl.createBranded(ReadableByteStreamController); + /** @type {() => void} */ + let startAlgorithm = () => undefined; + /** @type {() => Promise<void>} */ + let pullAlgorithm = () => resolvePromiseWith(undefined); + /** @type {(reason: any) => Promise<void>} */ + let cancelAlgorithm = (_reason) => resolvePromiseWith(undefined); + if (underlyingSourceDict.start !== undefined) { + startAlgorithm = () => + webidl.invokeCallbackFunction( + underlyingSourceDict.start, + [controller], + underlyingSource, + webidl.converters.any, + { + prefix: + "Failed to call 'startAlgorithm' on 'ReadableByteStreamController'", + }, + ); } - - /** - * @param {ReadableStream<ArrayBuffer>} stream - * @param {ReadableByteStreamController} controller - * @param {() => void} startAlgorithm - * @param {() => Promise<void>} pullAlgorithm - * @param {(reason: any) => Promise<void>} cancelAlgorithm - * @param {number} highWaterMark - * @param {number | undefined} autoAllocateChunkSize - */ - function setUpReadableByteStreamController( + if (underlyingSourceDict.pull !== undefined) { + pullAlgorithm = () => + webidl.invokeCallbackFunction( + underlyingSourceDict.pull, + [controller], + underlyingSource, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'pullAlgorithm' on 'ReadableByteStreamController'", + returnsPromise: true, + }, + ); + } + if (underlyingSourceDict.cancel !== undefined) { + cancelAlgorithm = (reason) => + webidl.invokeCallbackFunction( + underlyingSourceDict.cancel, + [reason], + underlyingSource, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'cancelAlgorithm' on 'ReadableByteStreamController'", + returnsPromise: true, + }, + ); + } + const autoAllocateChunkSize = underlyingSourceDict["autoAllocateChunkSize"]; + if (autoAllocateChunkSize === 0) { + throw new TypeError("autoAllocateChunkSize must be greater than 0"); + } + setUpReadableByteStreamController( stream, controller, startAlgorithm, @@ -3098,127 +3205,117 @@ cancelAlgorithm, highWaterMark, autoAllocateChunkSize, - ) { - assert(stream[_controller] === undefined); - if (autoAllocateChunkSize !== undefined) { - assert(NumberIsInteger(autoAllocateChunkSize)); - assert(autoAllocateChunkSize >= 0); - } - controller[_stream] = stream; - controller[_pullAgain] = controller[_pulling] = false; - controller[_byobRequest] = null; - resetQueue(controller); - controller[_closeRequested] = controller[_started] = false; - controller[_strategyHWM] = highWaterMark; - controller[_pullAlgorithm] = pullAlgorithm; - controller[_cancelAlgorithm] = cancelAlgorithm; - controller[_autoAllocateChunkSize] = autoAllocateChunkSize; - controller[_pendingPullIntos] = []; - stream[_controller] = controller; - const startResult = startAlgorithm(); - const startPromise = resolvePromiseWith(startResult); - setPromiseIsHandledToTrue( - PromisePrototypeThen( - startPromise, - () => { - controller[_started] = true; - assert(controller[_pulling] === false); - assert(controller[_pullAgain] === false); - readableByteStreamControllerCallPullIfNeeded(controller); + ); +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {ReadableStreamDefaultController<R>} controller + * @param {(controller: ReadableStreamDefaultController<R>) => void | Promise<void>} startAlgorithm + * @param {(controller: ReadableStreamDefaultController<R>) => Promise<void>} pullAlgorithm + * @param {(reason: any) => Promise<void>} cancelAlgorithm + * @param {number} highWaterMark + * @param {(chunk: R) => number} sizeAlgorithm + */ +function setUpReadableStreamDefaultController( + stream, + controller, + startAlgorithm, + pullAlgorithm, + cancelAlgorithm, + highWaterMark, + sizeAlgorithm, +) { + assert(stream[_controller] === undefined); + controller[_stream] = stream; + resetQueue(controller); + controller[_started] = + controller[_closeRequested] = + controller[_pullAgain] = + controller[_pulling] = + false; + controller[_strategySizeAlgorithm] = sizeAlgorithm; + controller[_strategyHWM] = highWaterMark; + controller[_pullAlgorithm] = pullAlgorithm; + controller[_cancelAlgorithm] = cancelAlgorithm; + stream[_controller] = controller; + const startResult = startAlgorithm(controller); + const startPromise = resolvePromiseWith(startResult); + uponPromise(startPromise, () => { + controller[_started] = true; + assert(controller[_pulling] === false); + assert(controller[_pullAgain] === false); + readableStreamDefaultControllerCallPullIfNeeded(controller); + }, (r) => { + readableStreamDefaultControllerError(controller, r); + }); +} + +/** + * @template R + * @param {ReadableStream<R>} stream + * @param {UnderlyingSource<R>} underlyingSource + * @param {UnderlyingSource<R>} underlyingSourceDict + * @param {number} highWaterMark + * @param {(chunk: R) => number} sizeAlgorithm + */ +function setUpReadableStreamDefaultControllerFromUnderlyingSource( + stream, + underlyingSource, + underlyingSourceDict, + highWaterMark, + sizeAlgorithm, +) { + const controller = webidl.createBranded(ReadableStreamDefaultController); + /** @type {() => Promise<void>} */ + let startAlgorithm = () => undefined; + /** @type {() => Promise<void>} */ + let pullAlgorithm = () => resolvePromiseWith(undefined); + /** @type {(reason?: any) => Promise<void>} */ + let cancelAlgorithm = () => resolvePromiseWith(undefined); + if (underlyingSourceDict.start !== undefined) { + startAlgorithm = () => + webidl.invokeCallbackFunction( + underlyingSourceDict.start, + [controller], + underlyingSource, + webidl.converters.any, + { + prefix: + "Failed to call 'startAlgorithm' on 'ReadableStreamDefaultController'", }, - (r) => { - readableByteStreamControllerError(controller, r); + ); + } + if (underlyingSourceDict.pull !== undefined) { + pullAlgorithm = () => + webidl.invokeCallbackFunction( + underlyingSourceDict.pull, + [controller], + underlyingSource, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'pullAlgorithm' on 'ReadableStreamDefaultController'", + returnsPromise: true, }, - ), - ); + ); } - - /** - * @param {ReadableStream<ArrayBuffer>} stream - * @param {UnderlyingSource<ArrayBuffer>} underlyingSource - * @param {UnderlyingSource<ArrayBuffer>} underlyingSourceDict - * @param {number} highWaterMark - */ - function setUpReadableByteStreamControllerFromUnderlyingSource( - stream, - underlyingSource, - underlyingSourceDict, - highWaterMark, - ) { - const controller = webidl.createBranded(ReadableByteStreamController); - /** @type {() => void} */ - let startAlgorithm = () => undefined; - /** @type {() => Promise<void>} */ - let pullAlgorithm = () => resolvePromiseWith(undefined); - /** @type {(reason: any) => Promise<void>} */ - let cancelAlgorithm = (_reason) => resolvePromiseWith(undefined); - if (underlyingSourceDict.start !== undefined) { - startAlgorithm = () => - webidl.invokeCallbackFunction( - underlyingSourceDict.start, - [controller], - underlyingSource, - webidl.converters.any, - { - prefix: - "Failed to call 'startAlgorithm' on 'ReadableByteStreamController'", - }, - ); - } - if (underlyingSourceDict.pull !== undefined) { - pullAlgorithm = () => - webidl.invokeCallbackFunction( - underlyingSourceDict.pull, - [controller], - underlyingSource, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'pullAlgorithm' on 'ReadableByteStreamController'", - returnsPromise: true, - }, - ); - } - if (underlyingSourceDict.cancel !== undefined) { - cancelAlgorithm = (reason) => - webidl.invokeCallbackFunction( - underlyingSourceDict.cancel, - [reason], - underlyingSource, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'cancelAlgorithm' on 'ReadableByteStreamController'", - returnsPromise: true, - }, - ); - } - const autoAllocateChunkSize = underlyingSourceDict["autoAllocateChunkSize"]; - if (autoAllocateChunkSize === 0) { - throw new TypeError("autoAllocateChunkSize must be greater than 0"); - } - setUpReadableByteStreamController( - stream, - controller, - startAlgorithm, - pullAlgorithm, - cancelAlgorithm, - highWaterMark, - autoAllocateChunkSize, - ); + if (underlyingSourceDict.cancel !== undefined) { + cancelAlgorithm = (reason) => + webidl.invokeCallbackFunction( + underlyingSourceDict.cancel, + [reason], + underlyingSource, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'cancelAlgorithm' on 'ReadableStreamDefaultController'", + returnsPromise: true, + }, + ); } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {ReadableStreamDefaultController<R>} controller - * @param {(controller: ReadableStreamDefaultController<R>) => void | Promise<void>} startAlgorithm - * @param {(controller: ReadableStreamDefaultController<R>) => Promise<void>} pullAlgorithm - * @param {(reason: any) => Promise<void>} cancelAlgorithm - * @param {number} highWaterMark - * @param {(chunk: R) => number} sizeAlgorithm - */ - function setUpReadableStreamDefaultController( + setUpReadableStreamDefaultController( stream, controller, startAlgorithm, @@ -3226,234 +3323,255 @@ cancelAlgorithm, highWaterMark, sizeAlgorithm, + ); +} + +/** + * @template R + * @param {ReadableStreamBYOBReader} reader + * @param {ReadableStream<R>} stream + */ +function setUpReadableStreamBYOBReader(reader, stream) { + if (isReadableStreamLocked(stream)) { + throw new TypeError("ReadableStream is locked."); + } + if ( + !(ObjectPrototypeIsPrototypeOf( + ReadableByteStreamControllerPrototype, + stream[_controller], + )) ) { - assert(stream[_controller] === undefined); - controller[_stream] = stream; - resetQueue(controller); - controller[_started] = - controller[_closeRequested] = - controller[_pullAgain] = - controller[_pulling] = - false; - controller[_strategySizeAlgorithm] = sizeAlgorithm; - controller[_strategyHWM] = highWaterMark; - controller[_pullAlgorithm] = pullAlgorithm; - controller[_cancelAlgorithm] = cancelAlgorithm; - stream[_controller] = controller; - const startResult = startAlgorithm(controller); - const startPromise = resolvePromiseWith(startResult); - uponPromise(startPromise, () => { - controller[_started] = true; - assert(controller[_pulling] === false); - assert(controller[_pullAgain] === false); - readableStreamDefaultControllerCallPullIfNeeded(controller); - }, (r) => { - readableStreamDefaultControllerError(controller, r); - }); - } - - /** - * @template R - * @param {ReadableStream<R>} stream - * @param {UnderlyingSource<R>} underlyingSource - * @param {UnderlyingSource<R>} underlyingSourceDict - * @param {number} highWaterMark - * @param {(chunk: R) => number} sizeAlgorithm - */ - function setUpReadableStreamDefaultControllerFromUnderlyingSource( - stream, - underlyingSource, - underlyingSourceDict, - highWaterMark, - sizeAlgorithm, - ) { - const controller = webidl.createBranded(ReadableStreamDefaultController); - /** @type {() => Promise<void>} */ - let startAlgorithm = () => undefined; - /** @type {() => Promise<void>} */ - let pullAlgorithm = () => resolvePromiseWith(undefined); - /** @type {(reason?: any) => Promise<void>} */ - let cancelAlgorithm = () => resolvePromiseWith(undefined); - if (underlyingSourceDict.start !== undefined) { - startAlgorithm = () => - webidl.invokeCallbackFunction( - underlyingSourceDict.start, - [controller], - underlyingSource, - webidl.converters.any, - { - prefix: - "Failed to call 'startAlgorithm' on 'ReadableStreamDefaultController'", - }, - ); - } - if (underlyingSourceDict.pull !== undefined) { - pullAlgorithm = () => - webidl.invokeCallbackFunction( - underlyingSourceDict.pull, - [controller], - underlyingSource, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'pullAlgorithm' on 'ReadableStreamDefaultController'", - returnsPromise: true, - }, - ); - } - if (underlyingSourceDict.cancel !== undefined) { - cancelAlgorithm = (reason) => - webidl.invokeCallbackFunction( - underlyingSourceDict.cancel, - [reason], - underlyingSource, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'cancelAlgorithm' on 'ReadableStreamDefaultController'", - returnsPromise: true, - }, - ); - } - setUpReadableStreamDefaultController( - stream, - controller, - startAlgorithm, - pullAlgorithm, - cancelAlgorithm, - highWaterMark, - sizeAlgorithm, - ); - } - - /** - * @template R - * @param {ReadableStreamBYOBReader} reader - * @param {ReadableStream<R>} stream - */ - function setUpReadableStreamBYOBReader(reader, stream) { - if (isReadableStreamLocked(stream)) { - throw new TypeError("ReadableStream is locked."); - } - if ( - !(ObjectPrototypeIsPrototypeOf( - ReadableByteStreamControllerPrototype, - stream[_controller], - )) - ) { - throw new TypeError("Cannot use a BYOB reader with a non-byte stream"); + throw new TypeError("Cannot use a BYOB reader with a non-byte stream"); + } + readableStreamReaderGenericInitialize(reader, stream); + reader[_readIntoRequests] = []; +} + +/** + * @template R + * @param {ReadableStreamDefaultReader<R>} reader + * @param {ReadableStream<R>} stream + */ +function setUpReadableStreamDefaultReader(reader, stream) { + if (isReadableStreamLocked(stream)) { + throw new TypeError("ReadableStream is locked."); + } + readableStreamReaderGenericInitialize(reader, stream); + reader[_readRequests] = []; +} + +/** + * @template O + * @param {TransformStream<any, O>} stream + * @param {TransformStreamDefaultController<O>} controller + * @param {(chunk: O, controller: TransformStreamDefaultController<O>) => Promise<void>} transformAlgorithm + * @param {(controller: TransformStreamDefaultController<O>) => Promise<void>} flushAlgorithm + */ +function setUpTransformStreamDefaultController( + stream, + controller, + transformAlgorithm, + flushAlgorithm, +) { + assert(ObjectPrototypeIsPrototypeOf(TransformStreamPrototype, stream)); + assert(stream[_controller] === undefined); + controller[_stream] = stream; + stream[_controller] = controller; + controller[_transformAlgorithm] = transformAlgorithm; + controller[_flushAlgorithm] = flushAlgorithm; +} + +/** + * @template I + * @template O + * @param {TransformStream<I, O>} stream + * @param {Transformer<I, O>} transformer + * @param {Transformer<I, O>} transformerDict + */ +function setUpTransformStreamDefaultControllerFromTransformer( + stream, + transformer, + transformerDict, +) { + /** @type {TransformStreamDefaultController<O>} */ + const controller = webidl.createBranded(TransformStreamDefaultController); + /** @type {(chunk: O, controller: TransformStreamDefaultController<O>) => Promise<void>} */ + let transformAlgorithm = (chunk) => { + try { + transformStreamDefaultControllerEnqueue(controller, chunk); + } catch (e) { + return PromiseReject(e); } - readableStreamReaderGenericInitialize(reader, stream); - reader[_readIntoRequests] = []; + return resolvePromiseWith(undefined); + }; + /** @type {(controller: TransformStreamDefaultController<O>) => Promise<void>} */ + let flushAlgorithm = () => resolvePromiseWith(undefined); + if (transformerDict.transform !== undefined) { + transformAlgorithm = (chunk, controller) => + webidl.invokeCallbackFunction( + transformerDict.transform, + [chunk, controller], + transformer, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'transformAlgorithm' on 'TransformStreamDefaultController'", + returnsPromise: true, + }, + ); } - - /** - * @template R - * @param {ReadableStreamDefaultReader<R>} reader - * @param {ReadableStream<R>} stream - */ - function setUpReadableStreamDefaultReader(reader, stream) { - if (isReadableStreamLocked(stream)) { - throw new TypeError("ReadableStream is locked."); - } - readableStreamReaderGenericInitialize(reader, stream); - reader[_readRequests] = []; + if (transformerDict.flush !== undefined) { + flushAlgorithm = (controller) => + webidl.invokeCallbackFunction( + transformerDict.flush, + [controller], + transformer, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'flushAlgorithm' on 'TransformStreamDefaultController'", + returnsPromise: true, + }, + ); } - - /** - * @template O - * @param {TransformStream<any, O>} stream - * @param {TransformStreamDefaultController<O>} controller - * @param {(chunk: O, controller: TransformStreamDefaultController<O>) => Promise<void>} transformAlgorithm - * @param {(controller: TransformStreamDefaultController<O>) => Promise<void>} flushAlgorithm - */ - function setUpTransformStreamDefaultController( + setUpTransformStreamDefaultController( stream, controller, transformAlgorithm, flushAlgorithm, - ) { - assert(ObjectPrototypeIsPrototypeOf(TransformStreamPrototype, stream)); - assert(stream[_controller] === undefined); - controller[_stream] = stream; - stream[_controller] = controller; - controller[_transformAlgorithm] = transformAlgorithm; - controller[_flushAlgorithm] = flushAlgorithm; + ); +} + +/** + * @template W + * @param {WritableStream<W>} stream + * @param {WritableStreamDefaultController<W>} controller + * @param {(controller: WritableStreamDefaultController<W>) => Promise<void>} startAlgorithm + * @param {(chunk: W, controller: WritableStreamDefaultController<W>) => Promise<void>} writeAlgorithm + * @param {() => Promise<void>} closeAlgorithm + * @param {(reason?: any) => Promise<void>} abortAlgorithm + * @param {number} highWaterMark + * @param {(chunk: W) => number} sizeAlgorithm + */ +function setUpWritableStreamDefaultController( + stream, + controller, + startAlgorithm, + writeAlgorithm, + closeAlgorithm, + abortAlgorithm, + highWaterMark, + sizeAlgorithm, +) { + assert(isWritableStream(stream)); + assert(stream[_controller] === undefined); + controller[_stream] = stream; + stream[_controller] = controller; + resetQueue(controller); + controller[_signal] = newSignal(); + controller[_started] = false; + controller[_strategySizeAlgorithm] = sizeAlgorithm; + controller[_strategyHWM] = highWaterMark; + controller[_writeAlgorithm] = writeAlgorithm; + controller[_closeAlgorithm] = closeAlgorithm; + controller[_abortAlgorithm] = abortAlgorithm; + const backpressure = writableStreamDefaultControllerGetBackpressure( + controller, + ); + writableStreamUpdateBackpressure(stream, backpressure); + const startResult = startAlgorithm(controller); + const startPromise = resolvePromiseWith(startResult); + uponPromise(startPromise, () => { + assert(stream[_state] === "writable" || stream[_state] === "erroring"); + controller[_started] = true; + writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); + }, (r) => { + assert(stream[_state] === "writable" || stream[_state] === "erroring"); + controller[_started] = true; + writableStreamDealWithRejection(stream, r); + }); +} + +/** + * @template W + * @param {WritableStream<W>} stream + * @param {UnderlyingSink<W>} underlyingSink + * @param {UnderlyingSink<W>} underlyingSinkDict + * @param {number} highWaterMark + * @param {(chunk: W) => number} sizeAlgorithm + */ +function setUpWritableStreamDefaultControllerFromUnderlyingSink( + stream, + underlyingSink, + underlyingSinkDict, + highWaterMark, + sizeAlgorithm, +) { + const controller = webidl.createBranded(WritableStreamDefaultController); + /** @type {(controller: WritableStreamDefaultController<W>) => any} */ + let startAlgorithm = () => undefined; + /** @type {(chunk: W, controller: WritableStreamDefaultController<W>) => Promise<void>} */ + let writeAlgorithm = () => resolvePromiseWith(undefined); + let closeAlgorithm = () => resolvePromiseWith(undefined); + /** @type {(reason?: any) => Promise<void>} */ + let abortAlgorithm = () => resolvePromiseWith(undefined); + + if (underlyingSinkDict.start !== undefined) { + startAlgorithm = () => + webidl.invokeCallbackFunction( + underlyingSinkDict.start, + [controller], + underlyingSink, + webidl.converters.any, + { + prefix: + "Failed to call 'startAlgorithm' on 'WritableStreamDefaultController'", + }, + ); } - - /** - * @template I - * @template O - * @param {TransformStream<I, O>} stream - * @param {Transformer<I, O>} transformer - * @param {Transformer<I, O>} transformerDict - */ - function setUpTransformStreamDefaultControllerFromTransformer( - stream, - transformer, - transformerDict, - ) { - /** @type {TransformStreamDefaultController<O>} */ - const controller = webidl.createBranded(TransformStreamDefaultController); - /** @type {(chunk: O, controller: TransformStreamDefaultController<O>) => Promise<void>} */ - let transformAlgorithm = (chunk) => { - try { - transformStreamDefaultControllerEnqueue(controller, chunk); - } catch (e) { - return PromiseReject(e); - } - return resolvePromiseWith(undefined); - }; - /** @type {(controller: TransformStreamDefaultController<O>) => Promise<void>} */ - let flushAlgorithm = () => resolvePromiseWith(undefined); - if (transformerDict.transform !== undefined) { - transformAlgorithm = (chunk, controller) => - webidl.invokeCallbackFunction( - transformerDict.transform, - [chunk, controller], - transformer, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'transformAlgorithm' on 'TransformStreamDefaultController'", - returnsPromise: true, - }, - ); - } - if (transformerDict.flush !== undefined) { - flushAlgorithm = (controller) => - webidl.invokeCallbackFunction( - transformerDict.flush, - [controller], - transformer, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'flushAlgorithm' on 'TransformStreamDefaultController'", - returnsPromise: true, - }, - ); - } - setUpTransformStreamDefaultController( - stream, - controller, - transformAlgorithm, - flushAlgorithm, - ); + if (underlyingSinkDict.write !== undefined) { + writeAlgorithm = (chunk) => + webidl.invokeCallbackFunction( + underlyingSinkDict.write, + [chunk, controller], + underlyingSink, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'writeAlgorithm' on 'WritableStreamDefaultController'", + returnsPromise: true, + }, + ); } - - /** - * @template W - * @param {WritableStream<W>} stream - * @param {WritableStreamDefaultController<W>} controller - * @param {(controller: WritableStreamDefaultController<W>) => Promise<void>} startAlgorithm - * @param {(chunk: W, controller: WritableStreamDefaultController<W>) => Promise<void>} writeAlgorithm - * @param {() => Promise<void>} closeAlgorithm - * @param {(reason?: any) => Promise<void>} abortAlgorithm - * @param {number} highWaterMark - * @param {(chunk: W) => number} sizeAlgorithm - */ - function setUpWritableStreamDefaultController( + if (underlyingSinkDict.close !== undefined) { + closeAlgorithm = () => + webidl.invokeCallbackFunction( + underlyingSinkDict.close, + [], + underlyingSink, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'closeAlgorithm' on 'WritableStreamDefaultController'", + returnsPromise: true, + }, + ); + } + if (underlyingSinkDict.abort !== undefined) { + abortAlgorithm = (reason) => + webidl.invokeCallbackFunction( + underlyingSinkDict.abort, + [reason], + underlyingSink, + webidl.converters["Promise<undefined>"], + { + prefix: + "Failed to call 'abortAlgorithm' on 'WritableStreamDefaultController'", + returnsPromise: true, + }, + ); + } + setUpWritableStreamDefaultController( stream, controller, startAlgorithm, @@ -3462,2794 +3580,2671 @@ abortAlgorithm, highWaterMark, sizeAlgorithm, - ) { - assert(isWritableStream(stream)); - assert(stream[_controller] === undefined); - controller[_stream] = stream; - stream[_controller] = controller; - resetQueue(controller); - controller[_signal] = newSignal(); - controller[_started] = false; - controller[_strategySizeAlgorithm] = sizeAlgorithm; - controller[_strategyHWM] = highWaterMark; - controller[_writeAlgorithm] = writeAlgorithm; - controller[_closeAlgorithm] = closeAlgorithm; - controller[_abortAlgorithm] = abortAlgorithm; - const backpressure = writableStreamDefaultControllerGetBackpressure( - controller, - ); - writableStreamUpdateBackpressure(stream, backpressure); - const startResult = startAlgorithm(controller); - const startPromise = resolvePromiseWith(startResult); - uponPromise(startPromise, () => { - assert(stream[_state] === "writable" || stream[_state] === "erroring"); - controller[_started] = true; - writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); - }, (r) => { - assert(stream[_state] === "writable" || stream[_state] === "erroring"); - controller[_started] = true; - writableStreamDealWithRejection(stream, r); - }); - } - - /** - * @template W - * @param {WritableStream<W>} stream - * @param {UnderlyingSink<W>} underlyingSink - * @param {UnderlyingSink<W>} underlyingSinkDict - * @param {number} highWaterMark - * @param {(chunk: W) => number} sizeAlgorithm - */ - function setUpWritableStreamDefaultControllerFromUnderlyingSink( - stream, - underlyingSink, - underlyingSinkDict, - highWaterMark, - sizeAlgorithm, - ) { - const controller = webidl.createBranded(WritableStreamDefaultController); - /** @type {(controller: WritableStreamDefaultController<W>) => any} */ - let startAlgorithm = () => undefined; - /** @type {(chunk: W, controller: WritableStreamDefaultController<W>) => Promise<void>} */ - let writeAlgorithm = () => resolvePromiseWith(undefined); - let closeAlgorithm = () => resolvePromiseWith(undefined); - /** @type {(reason?: any) => Promise<void>} */ - let abortAlgorithm = () => resolvePromiseWith(undefined); - - if (underlyingSinkDict.start !== undefined) { - startAlgorithm = () => - webidl.invokeCallbackFunction( - underlyingSinkDict.start, - [controller], - underlyingSink, - webidl.converters.any, - { - prefix: - "Failed to call 'startAlgorithm' on 'WritableStreamDefaultController'", - }, - ); - } - if (underlyingSinkDict.write !== undefined) { - writeAlgorithm = (chunk) => - webidl.invokeCallbackFunction( - underlyingSinkDict.write, - [chunk, controller], - underlyingSink, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'writeAlgorithm' on 'WritableStreamDefaultController'", - returnsPromise: true, - }, - ); - } - if (underlyingSinkDict.close !== undefined) { - closeAlgorithm = () => - webidl.invokeCallbackFunction( - underlyingSinkDict.close, - [], - underlyingSink, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'closeAlgorithm' on 'WritableStreamDefaultController'", - returnsPromise: true, - }, - ); - } - if (underlyingSinkDict.abort !== undefined) { - abortAlgorithm = (reason) => - webidl.invokeCallbackFunction( - underlyingSinkDict.abort, - [reason], - underlyingSink, - webidl.converters["Promise<undefined>"], - { - prefix: - "Failed to call 'abortAlgorithm' on 'WritableStreamDefaultController'", - returnsPromise: true, - }, - ); - } - setUpWritableStreamDefaultController( - stream, - controller, - startAlgorithm, - writeAlgorithm, - closeAlgorithm, - abortAlgorithm, - highWaterMark, - sizeAlgorithm, - ); - } - - /** - * @template W - * @param {WritableStreamDefaultWriter<W>} writer - * @param {WritableStream<W>} stream - */ - function setUpWritableStreamDefaultWriter(writer, stream) { - if (isWritableStreamLocked(stream) === true) { - throw new TypeError("The stream is already locked."); - } - writer[_stream] = stream; - stream[_writer] = writer; - const state = stream[_state]; - if (state === "writable") { - if ( - writableStreamCloseQueuedOrInFlight(stream) === false && - stream[_backpressure] === true - ) { - writer[_readyPromise] = new Deferred(); - } else { - writer[_readyPromise] = new Deferred(); - writer[_readyPromise].resolve(undefined); - } - writer[_closedPromise] = new Deferred(); - } else if (state === "erroring") { - writer[_readyPromise] = new Deferred(); - writer[_readyPromise].reject(stream[_storedError]); - setPromiseIsHandledToTrue(writer[_readyPromise].promise); - writer[_closedPromise] = new Deferred(); - } else if (state === "closed") { + ); +} + +/** + * @template W + * @param {WritableStreamDefaultWriter<W>} writer + * @param {WritableStream<W>} stream + */ +function setUpWritableStreamDefaultWriter(writer, stream) { + if (isWritableStreamLocked(stream) === true) { + throw new TypeError("The stream is already locked."); + } + writer[_stream] = stream; + stream[_writer] = writer; + const state = stream[_state]; + if (state === "writable") { + if ( + writableStreamCloseQueuedOrInFlight(stream) === false && + stream[_backpressure] === true + ) { writer[_readyPromise] = new Deferred(); - writer[_readyPromise].resolve(undefined); - writer[_closedPromise] = new Deferred(); - writer[_closedPromise].resolve(undefined); } else { - assert(state === "errored"); - const storedError = stream[_storedError]; writer[_readyPromise] = new Deferred(); - writer[_readyPromise].reject(storedError); - setPromiseIsHandledToTrue(writer[_readyPromise].promise); - writer[_closedPromise] = new Deferred(); - writer[_closedPromise].reject(storedError); - setPromiseIsHandledToTrue(writer[_closedPromise].promise); + writer[_readyPromise].resolve(undefined); } + writer[_closedPromise] = new Deferred(); + } else if (state === "erroring") { + writer[_readyPromise] = new Deferred(); + writer[_readyPromise].reject(stream[_storedError]); + setPromiseIsHandledToTrue(writer[_readyPromise].promise); + writer[_closedPromise] = new Deferred(); + } else if (state === "closed") { + writer[_readyPromise] = new Deferred(); + writer[_readyPromise].resolve(undefined); + writer[_closedPromise] = new Deferred(); + writer[_closedPromise].resolve(undefined); + } else { + assert(state === "errored"); + const storedError = stream[_storedError]; + writer[_readyPromise] = new Deferred(); + writer[_readyPromise].reject(storedError); + setPromiseIsHandledToTrue(writer[_readyPromise].promise); + writer[_closedPromise] = new Deferred(); + writer[_closedPromise].reject(storedError); + setPromiseIsHandledToTrue(writer[_closedPromise].promise); } - - /** @param {TransformStreamDefaultController} controller */ - function transformStreamDefaultControllerClearAlgorithms(controller) { - controller[_transformAlgorithm] = undefined; - controller[_flushAlgorithm] = undefined; +} + +/** @param {TransformStreamDefaultController} controller */ +function transformStreamDefaultControllerClearAlgorithms(controller) { + controller[_transformAlgorithm] = undefined; + controller[_flushAlgorithm] = undefined; +} + +/** + * @template O + * @param {TransformStreamDefaultController<O>} controller + * @param {O} chunk + */ +function transformStreamDefaultControllerEnqueue(controller, chunk) { + const stream = controller[_stream]; + const readableController = stream[_readable][_controller]; + if ( + readableStreamDefaultControllerCanCloseOrEnqueue( + /** @type {ReadableStreamDefaultController<O>} */ readableController, + ) === false + ) { + throw new TypeError("Readable stream is unavailable."); } - - /** - * @template O - * @param {TransformStreamDefaultController<O>} controller - * @param {O} chunk - */ - function transformStreamDefaultControllerEnqueue(controller, chunk) { - const stream = controller[_stream]; - const readableController = stream[_readable][_controller]; - if ( - readableStreamDefaultControllerCanCloseOrEnqueue( - /** @type {ReadableStreamDefaultController<O>} */ readableController, - ) === false - ) { - throw new TypeError("Readable stream is unavailable."); - } - try { - readableStreamDefaultControllerEnqueue( - /** @type {ReadableStreamDefaultController<O>} */ readableController, - chunk, - ); - } catch (e) { - transformStreamErrorWritableAndUnblockWrite(stream, e); - throw stream[_readable][_storedError]; - } - const backpressure = readableStreamDefaultcontrollerHasBackpressure( + try { + readableStreamDefaultControllerEnqueue( /** @type {ReadableStreamDefaultController<O>} */ readableController, + chunk, ); - if (backpressure !== stream[_backpressure]) { - assert(backpressure === true); - transformStreamSetBackpressure(stream, true); - } - } - - /** - * @param {TransformStreamDefaultController} controller - * @param {any=} e - */ - function transformStreamDefaultControllerError(controller, e) { - transformStreamError(controller[_stream], e); + } catch (e) { + transformStreamErrorWritableAndUnblockWrite(stream, e); + throw stream[_readable][_storedError]; } - - /** - * @template O - * @param {TransformStreamDefaultController<O>} controller - * @param {any} chunk - * @returns {Promise<void>} - */ - function transformStreamDefaultControllerPerformTransform(controller, chunk) { - const transformPromise = controller[_transformAlgorithm](chunk, controller); - return transformPromiseWith(transformPromise, undefined, (r) => { - transformStreamError(controller[_stream], r); - throw r; - }); + const backpressure = readableStreamDefaultcontrollerHasBackpressure( + /** @type {ReadableStreamDefaultController<O>} */ readableController, + ); + if (backpressure !== stream[_backpressure]) { + assert(backpressure === true); + transformStreamSetBackpressure(stream, true); } - - /** @param {TransformStreamDefaultController} controller */ - function transformStreamDefaultControllerTerminate(controller) { - const stream = controller[_stream]; - const readableController = stream[_readable][_controller]; +} + +/** + * @param {TransformStreamDefaultController} controller + * @param {any=} e + */ +function transformStreamDefaultControllerError(controller, e) { + transformStreamError(controller[_stream], e); +} + +/** + * @template O + * @param {TransformStreamDefaultController<O>} controller + * @param {any} chunk + * @returns {Promise<void>} + */ +function transformStreamDefaultControllerPerformTransform(controller, chunk) { + const transformPromise = controller[_transformAlgorithm](chunk, controller); + return transformPromiseWith(transformPromise, undefined, (r) => { + transformStreamError(controller[_stream], r); + throw r; + }); +} + +/** @param {TransformStreamDefaultController} controller */ +function transformStreamDefaultControllerTerminate(controller) { + const stream = controller[_stream]; + const readableController = stream[_readable][_controller]; + readableStreamDefaultControllerClose( + /** @type {ReadableStreamDefaultController} */ readableController, + ); + const error = new TypeError("The stream has been terminated."); + transformStreamErrorWritableAndUnblockWrite(stream, error); +} + +/** + * @param {TransformStream} stream + * @param {any=} reason + * @returns {Promise<void>} + */ +function transformStreamDefaultSinkAbortAlgorithm(stream, reason) { + transformStreamError(stream, reason); + return resolvePromiseWith(undefined); +} + +/** + * @template I + * @template O + * @param {TransformStream<I, O>} stream + * @returns {Promise<void>} + */ +function transformStreamDefaultSinkCloseAlgorithm(stream) { + const readable = stream[_readable]; + const controller = stream[_controller]; + const flushPromise = controller[_flushAlgorithm](controller); + transformStreamDefaultControllerClearAlgorithms(controller); + return transformPromiseWith(flushPromise, () => { + if (readable[_state] === "errored") { + throw readable[_storedError]; + } readableStreamDefaultControllerClose( - /** @type {ReadableStreamDefaultController} */ readableController, + /** @type {ReadableStreamDefaultController} */ readable[_controller], ); - const error = new TypeError("The stream has been terminated."); - transformStreamErrorWritableAndUnblockWrite(stream, error); - } - - /** - * @param {TransformStream} stream - * @param {any=} reason - * @returns {Promise<void>} - */ - function transformStreamDefaultSinkAbortAlgorithm(stream, reason) { - transformStreamError(stream, reason); - return resolvePromiseWith(undefined); - } - - /** - * @template I - * @template O - * @param {TransformStream<I, O>} stream - * @returns {Promise<void>} - */ - function transformStreamDefaultSinkCloseAlgorithm(stream) { - const readable = stream[_readable]; - const controller = stream[_controller]; - const flushPromise = controller[_flushAlgorithm](controller); - transformStreamDefaultControllerClearAlgorithms(controller); - return transformPromiseWith(flushPromise, () => { - if (readable[_state] === "errored") { - throw readable[_storedError]; - } - readableStreamDefaultControllerClose( - /** @type {ReadableStreamDefaultController} */ readable[_controller], + }, (r) => { + transformStreamError(stream, r); + throw readable[_storedError]; + }); +} + +/** + * @template I + * @template O + * @param {TransformStream<I, O>} stream + * @param {I} chunk + * @returns {Promise<void>} + */ +function transformStreamDefaultSinkWriteAlgorithm(stream, chunk) { + assert(stream[_writable][_state] === "writable"); + const controller = stream[_controller]; + if (stream[_backpressure] === true) { + const backpressureChangePromise = stream[_backpressureChangePromise]; + assert(backpressureChangePromise !== undefined); + return transformPromiseWith(backpressureChangePromise.promise, () => { + const writable = stream[_writable]; + const state = writable[_state]; + if (state === "erroring") { + throw writable[_storedError]; + } + assert(state === "writable"); + return transformStreamDefaultControllerPerformTransform( + controller, + chunk, ); - }, (r) => { - transformStreamError(stream, r); - throw readable[_storedError]; }); } - - /** - * @template I - * @template O - * @param {TransformStream<I, O>} stream - * @param {I} chunk - * @returns {Promise<void>} - */ - function transformStreamDefaultSinkWriteAlgorithm(stream, chunk) { - assert(stream[_writable][_state] === "writable"); - const controller = stream[_controller]; - if (stream[_backpressure] === true) { - const backpressureChangePromise = stream[_backpressureChangePromise]; - assert(backpressureChangePromise !== undefined); - return transformPromiseWith(backpressureChangePromise.promise, () => { - const writable = stream[_writable]; - const state = writable[_state]; - if (state === "erroring") { - throw writable[_storedError]; - } - assert(state === "writable"); - return transformStreamDefaultControllerPerformTransform( - controller, - chunk, - ); - }); - } - return transformStreamDefaultControllerPerformTransform(controller, chunk); - } - - /** - * @param {TransformStream} stream - * @returns {Promise<void>} - */ - function transformStreamDefaultSourcePullAlgorithm(stream) { - assert(stream[_backpressure] === true); - assert(stream[_backpressureChangePromise] !== undefined); + return transformStreamDefaultControllerPerformTransform(controller, chunk); +} + +/** + * @param {TransformStream} stream + * @returns {Promise<void>} + */ +function transformStreamDefaultSourcePullAlgorithm(stream) { + assert(stream[_backpressure] === true); + assert(stream[_backpressureChangePromise] !== undefined); + transformStreamSetBackpressure(stream, false); + return stream[_backpressureChangePromise].promise; +} + +/** + * @param {TransformStream} stream + * @param {any=} e + */ +function transformStreamError(stream, e) { + readableStreamDefaultControllerError( + /** @type {ReadableStreamDefaultController} */ stream[_readable][ + _controller + ], + e, + ); + transformStreamErrorWritableAndUnblockWrite(stream, e); +} + +/** + * @param {TransformStream} stream + * @param {any=} e + */ +function transformStreamErrorWritableAndUnblockWrite(stream, e) { + transformStreamDefaultControllerClearAlgorithms(stream[_controller]); + writableStreamDefaultControllerErrorIfNeeded( + stream[_writable][_controller], + e, + ); + if (stream[_backpressure] === true) { transformStreamSetBackpressure(stream, false); - return stream[_backpressureChangePromise].promise; } - - /** - * @param {TransformStream} stream - * @param {any=} e - */ - function transformStreamError(stream, e) { - readableStreamDefaultControllerError( - /** @type {ReadableStreamDefaultController} */ stream[_readable][ - _controller - ], - e, - ); - transformStreamErrorWritableAndUnblockWrite(stream, e); - } - - /** - * @param {TransformStream} stream - * @param {any=} e - */ - function transformStreamErrorWritableAndUnblockWrite(stream, e) { - transformStreamDefaultControllerClearAlgorithms(stream[_controller]); - writableStreamDefaultControllerErrorIfNeeded( - stream[_writable][_controller], - e, - ); - if (stream[_backpressure] === true) { - transformStreamSetBackpressure(stream, false); - } +} + +/** + * @param {TransformStream} stream + * @param {boolean} backpressure + */ +function transformStreamSetBackpressure(stream, backpressure) { + assert(stream[_backpressure] !== backpressure); + if (stream[_backpressureChangePromise] !== undefined) { + stream[_backpressureChangePromise].resolve(undefined); + } + stream[_backpressureChangePromise] = new Deferred(); + stream[_backpressure] = backpressure; +} + +/** + * @param {WritableStream} stream + * @param {any=} reason + * @returns {Promise<void>} + */ +function writableStreamAbort(stream, reason) { + const state = stream[_state]; + if (state === "closed" || state === "errored") { + return resolvePromiseWith(undefined); } - - /** - * @param {TransformStream} stream - * @param {boolean} backpressure - */ - function transformStreamSetBackpressure(stream, backpressure) { - assert(stream[_backpressure] !== backpressure); - if (stream[_backpressureChangePromise] !== undefined) { - stream[_backpressureChangePromise].resolve(undefined); - } - stream[_backpressureChangePromise] = new Deferred(); - stream[_backpressure] = backpressure; + stream[_controller][_signal][signalAbort](reason); + if (state === "closed" || state === "errored") { + return resolvePromiseWith(undefined); } - - /** - * @param {WritableStream} stream - * @param {any=} reason - * @returns {Promise<void>} - */ - function writableStreamAbort(stream, reason) { - const state = stream[_state]; - if (state === "closed" || state === "errored") { - return resolvePromiseWith(undefined); - } - stream[_controller][_signal][signalAbort](reason); - if (state === "closed" || state === "errored") { - return resolvePromiseWith(undefined); - } - if (stream[_pendingAbortRequest] !== undefined) { - return stream[_pendingAbortRequest].deferred.promise; - } - assert(state === "writable" || state === "erroring"); - let wasAlreadyErroring = false; - if (state === "erroring") { - wasAlreadyErroring = true; - reason = undefined; - } - /** Deferred<void> */ - const deferred = new Deferred(); - stream[_pendingAbortRequest] = { - deferred, - reason, - wasAlreadyErroring, - }; - if (wasAlreadyErroring === false) { - writableStreamStartErroring(stream, reason); - } - return deferred.promise; + if (stream[_pendingAbortRequest] !== undefined) { + return stream[_pendingAbortRequest].deferred.promise; + } + assert(state === "writable" || state === "erroring"); + let wasAlreadyErroring = false; + if (state === "erroring") { + wasAlreadyErroring = true; + reason = undefined; + } + /** Deferred<void> */ + const deferred = new Deferred(); + stream[_pendingAbortRequest] = { + deferred, + reason, + wasAlreadyErroring, + }; + if (wasAlreadyErroring === false) { + writableStreamStartErroring(stream, reason); + } + return deferred.promise; +} + +/** + * @param {WritableStream} stream + * @returns {Promise<void>} + */ +function writableStreamAddWriteRequest(stream) { + assert(isWritableStreamLocked(stream) === true); + assert(stream[_state] === "writable"); + /** @type {Deferred<void>} */ + const deferred = new Deferred(); + ArrayPrototypePush(stream[_writeRequests], deferred); + return deferred.promise; +} + +/** + * @param {WritableStream} stream + * @returns {Promise<void>} + */ +function writableStreamClose(stream) { + const state = stream[_state]; + if (state === "closed" || state === "errored") { + return PromiseReject( + new TypeError("Writable stream is closed or errored."), + ); } - - /** - * @param {WritableStream} stream - * @returns {Promise<void>} - */ - function writableStreamAddWriteRequest(stream) { - assert(isWritableStreamLocked(stream) === true); - assert(stream[_state] === "writable"); - /** @type {Deferred<void>} */ - const deferred = new Deferred(); - ArrayPrototypePush(stream[_writeRequests], deferred); - return deferred.promise; + assert(state === "writable" || state === "erroring"); + assert(writableStreamCloseQueuedOrInFlight(stream) === false); + /** @type {Deferred<void>} */ + const deferred = new Deferred(); + stream[_closeRequest] = deferred; + const writer = stream[_writer]; + if ( + writer !== undefined && stream[_backpressure] === true && + state === "writable" + ) { + writer[_readyPromise].resolve(undefined); + } + writableStreamDefaultControllerClose(stream[_controller]); + return deferred.promise; +} + +/** + * @param {WritableStream} stream + * @returns {boolean} + */ +function writableStreamCloseQueuedOrInFlight(stream) { + if ( + stream[_closeRequest] === undefined && + stream[_inFlightCloseRequest] === undefined + ) { + return false; } + return true; +} - /** - * @param {WritableStream} stream - * @returns {Promise<void>} - */ - function writableStreamClose(stream) { +/** + * @param {WritableStream} stream + * @param {any=} error + */ +function writableStreamDealWithRejection(stream, error) { + const state = stream[_state]; + if (state === "writable") { + writableStreamStartErroring(stream, error); + return; + } + assert(state === "erroring"); + writableStreamFinishErroring(stream); +} + +/** + * @template W + * @param {WritableStreamDefaultController<W>} controller + */ +function writableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { + const stream = controller[_stream]; + if (controller[_started] === false) { + return; + } + if (stream[_inFlightWriteRequest] !== undefined) { + return; + } + const state = stream[_state]; + assert(state !== "closed" && state !== "errored"); + if (state === "erroring") { + writableStreamFinishErroring(stream); + return; + } + if (controller[_queue].length === 0) { + return; + } + const value = peekQueueValue(controller); + if (value === _close) { + writableStreamDefaultControllerProcessClose(controller); + } else { + writableStreamDefaultControllerProcessWrite(controller, value); + } +} + +function writableStreamDefaultControllerClearAlgorithms(controller) { + controller[_writeAlgorithm] = undefined; + controller[_closeAlgorithm] = undefined; + controller[_abortAlgorithm] = undefined; + controller[_strategySizeAlgorithm] = undefined; +} + +/** @param {WritableStreamDefaultController} controller */ +function writableStreamDefaultControllerClose(controller) { + enqueueValueWithSize(controller, _close, 0); + writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); +} + +/** + * @param {WritableStreamDefaultController} controller + * @param {any} error + */ +function writableStreamDefaultControllerError(controller, error) { + const stream = controller[_stream]; + assert(stream[_state] === "writable"); + writableStreamDefaultControllerClearAlgorithms(controller); + writableStreamStartErroring(stream, error); +} + +/** + * @param {WritableStreamDefaultController} controller + * @param {any} error + */ +function writableStreamDefaultControllerErrorIfNeeded(controller, error) { + if (controller[_stream][_state] === "writable") { + writableStreamDefaultControllerError(controller, error); + } +} + +/** + * @param {WritableStreamDefaultController} controller + * @returns {boolean} + */ +function writableStreamDefaultControllerGetBackpressure(controller) { + const desiredSize = writableStreamDefaultControllerGetDesiredSize( + controller, + ); + return desiredSize <= 0; +} + +/** + * @template W + * @param {WritableStreamDefaultController<W>} controller + * @param {W} chunk + * @returns {number} + */ +function writableStreamDefaultControllerGetChunkSize(controller, chunk) { + let value; + try { + value = controller[_strategySizeAlgorithm](chunk); + } catch (e) { + writableStreamDefaultControllerErrorIfNeeded(controller, e); + return 1; + } + return value; +} + +/** + * @param {WritableStreamDefaultController} controller + * @returns {number} + */ +function writableStreamDefaultControllerGetDesiredSize(controller) { + return controller[_strategyHWM] - controller[_queueTotalSize]; +} + +/** @param {WritableStreamDefaultController} controller */ +function writableStreamDefaultControllerProcessClose(controller) { + const stream = controller[_stream]; + writableStreamMarkCloseRequestInFlight(stream); + dequeueValue(controller); + assert(controller[_queue].length === 0); + const sinkClosePromise = controller[_closeAlgorithm](); + writableStreamDefaultControllerClearAlgorithms(controller); + uponPromise(sinkClosePromise, () => { + writableStreamFinishInFlightClose(stream); + }, (reason) => { + writableStreamFinishInFlightCloseWithError(stream, reason); + }); +} + +/** + * @template W + * @param {WritableStreamDefaultController<W>} controller + * @param {W} chunk + */ +function writableStreamDefaultControllerProcessWrite(controller, chunk) { + const stream = controller[_stream]; + writableStreamMarkFirstWriteRequestInFlight(stream); + const sinkWritePromise = controller[_writeAlgorithm](chunk, controller); + uponPromise(sinkWritePromise, () => { + writableStreamFinishInFlightWrite(stream); const state = stream[_state]; - if (state === "closed" || state === "errored") { - return PromiseReject( - new TypeError("Writable stream is closed or errored."), - ); - } assert(state === "writable" || state === "erroring"); - assert(writableStreamCloseQueuedOrInFlight(stream) === false); - /** @type {Deferred<void>} */ - const deferred = new Deferred(); - stream[_closeRequest] = deferred; - const writer = stream[_writer]; + dequeueValue(controller); if ( - writer !== undefined && stream[_backpressure] === true && + writableStreamCloseQueuedOrInFlight(stream) === false && state === "writable" ) { - writer[_readyPromise].resolve(undefined); + const backpressure = writableStreamDefaultControllerGetBackpressure( + controller, + ); + writableStreamUpdateBackpressure(stream, backpressure); } - writableStreamDefaultControllerClose(stream[_controller]); - return deferred.promise; - } - - /** - * @param {WritableStream} stream - * @returns {boolean} - */ - function writableStreamCloseQueuedOrInFlight(stream) { - if ( - stream[_closeRequest] === undefined && - stream[_inFlightCloseRequest] === undefined - ) { - return false; + writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); + }, (reason) => { + if (stream[_state] === "writable") { + writableStreamDefaultControllerClearAlgorithms(controller); } - return true; + writableStreamFinishInFlightWriteWithError(stream, reason); + }); +} + +/** + * @template W + * @param {WritableStreamDefaultController<W>} controller + * @param {W} chunk + * @param {number} chunkSize + */ +function writableStreamDefaultControllerWrite(controller, chunk, chunkSize) { + try { + enqueueValueWithSize(controller, chunk, chunkSize); + } catch (e) { + writableStreamDefaultControllerErrorIfNeeded(controller, e); + return; + } + const stream = controller[_stream]; + if ( + writableStreamCloseQueuedOrInFlight(stream) === false && + stream[_state] === "writable" + ) { + const backpressure = writableStreamDefaultControllerGetBackpressure( + controller, + ); + writableStreamUpdateBackpressure(stream, backpressure); } - - /** - * @param {WritableStream} stream - * @param {any=} error - */ - function writableStreamDealWithRejection(stream, error) { - const state = stream[_state]; - if (state === "writable") { - writableStreamStartErroring(stream, error); - return; + writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); +} + +/** + * @param {WritableStreamDefaultWriter} writer + * @param {any=} reason + * @returns {Promise<void>} + */ +function writableStreamDefaultWriterAbort(writer, reason) { + const stream = writer[_stream]; + assert(stream !== undefined); + return writableStreamAbort(stream, reason); +} + +/** + * @param {WritableStreamDefaultWriter} writer + * @returns {Promise<void>} + */ +function writableStreamDefaultWriterClose(writer) { + const stream = writer[_stream]; + assert(stream !== undefined); + return writableStreamClose(stream); +} + +/** + * @param {WritableStreamDefaultWriter} writer + * @returns {Promise<void>} + */ +function writableStreamDefaultWriterCloseWithErrorPropagation(writer) { + const stream = writer[_stream]; + assert(stream !== undefined); + const state = stream[_state]; + if ( + writableStreamCloseQueuedOrInFlight(stream) === true || state === "closed" + ) { + return resolvePromiseWith(undefined); + } + if (state === "errored") { + return PromiseReject(stream[_storedError]); + } + assert(state === "writable" || state === "erroring"); + return writableStreamDefaultWriterClose(writer); +} + +/** + * @param {WritableStreamDefaultWriter} writer + * @param {any=} error + */ +function writableStreamDefaultWriterEnsureClosedPromiseRejected( + writer, + error, +) { + if (writer[_closedPromise].state === "pending") { + writer[_closedPromise].reject(error); + } else { + writer[_closedPromise] = new Deferred(); + writer[_closedPromise].reject(error); + } + setPromiseIsHandledToTrue(writer[_closedPromise].promise); +} + +/** + * @param {WritableStreamDefaultWriter} writer + * @param {any=} error + */ +function writableStreamDefaultWriterEnsureReadyPromiseRejected( + writer, + error, +) { + if (writer[_readyPromise].state === "pending") { + writer[_readyPromise].reject(error); + } else { + writer[_readyPromise] = new Deferred(); + writer[_readyPromise].reject(error); + } + setPromiseIsHandledToTrue(writer[_readyPromise].promise); +} + +/** + * @param {WritableStreamDefaultWriter} writer + * @returns {number | null} + */ +function writableStreamDefaultWriterGetDesiredSize(writer) { + const stream = writer[_stream]; + const state = stream[_state]; + if (state === "errored" || state === "erroring") { + return null; + } + if (state === "closed") { + return 0; + } + return writableStreamDefaultControllerGetDesiredSize(stream[_controller]); +} + +/** @param {WritableStreamDefaultWriter} writer */ +function writableStreamDefaultWriterRelease(writer) { + const stream = writer[_stream]; + assert(stream !== undefined); + assert(stream[_writer] === writer); + const releasedError = new TypeError( + "The writer has already been released.", + ); + writableStreamDefaultWriterEnsureReadyPromiseRejected( + writer, + releasedError, + ); + writableStreamDefaultWriterEnsureClosedPromiseRejected( + writer, + releasedError, + ); + stream[_writer] = undefined; + writer[_stream] = undefined; +} + +/** + * @template W + * @param {WritableStreamDefaultWriter<W>} writer + * @param {W} chunk + * @returns {Promise<void>} + */ +function writableStreamDefaultWriterWrite(writer, chunk) { + const stream = writer[_stream]; + assert(stream !== undefined); + const controller = stream[_controller]; + const chunkSize = writableStreamDefaultControllerGetChunkSize( + controller, + chunk, + ); + if (stream !== writer[_stream]) { + return PromiseReject(new TypeError("Writer's stream is unexpected.")); + } + const state = stream[_state]; + if (state === "errored") { + return PromiseReject(stream[_storedError]); + } + if ( + writableStreamCloseQueuedOrInFlight(stream) === true || state === "closed" + ) { + return PromiseReject( + new TypeError("The stream is closing or is closed."), + ); + } + if (state === "erroring") { + return PromiseReject(stream[_storedError]); + } + assert(state === "writable"); + const promise = writableStreamAddWriteRequest(stream); + writableStreamDefaultControllerWrite(controller, chunk, chunkSize); + return promise; +} + +/** @param {WritableStream} stream */ +function writableStreamFinishErroring(stream) { + assert(stream[_state] === "erroring"); + assert(writableStreamHasOperationMarkedInFlight(stream) === false); + stream[_state] = "errored"; + stream[_controller][_errorSteps](); + const storedError = stream[_storedError]; + const writeRequests = stream[_writeRequests]; + for (let i = 0; i < writeRequests.length; ++i) { + const writeRequest = writeRequests[i]; + writeRequest.reject(storedError); + } + stream[_writeRequests] = []; + if (stream[_pendingAbortRequest] === undefined) { + writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + return; + } + const abortRequest = stream[_pendingAbortRequest]; + stream[_pendingAbortRequest] = undefined; + if (abortRequest.wasAlreadyErroring === true) { + abortRequest.deferred.reject(storedError); + writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + return; + } + const promise = stream[_controller][_abortSteps](abortRequest.reason); + uponPromise(promise, () => { + abortRequest.deferred.resolve(undefined); + writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + }, (reason) => { + abortRequest.deferred.reject(reason); + writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + }); +} + +/** @param {WritableStream} stream */ +function writableStreamFinishInFlightClose(stream) { + assert(stream[_inFlightCloseRequest] !== undefined); + stream[_inFlightCloseRequest].resolve(undefined); + stream[_inFlightCloseRequest] = undefined; + const state = stream[_state]; + assert(state === "writable" || state === "erroring"); + if (state === "erroring") { + stream[_storedError] = undefined; + if (stream[_pendingAbortRequest] !== undefined) { + stream[_pendingAbortRequest].deferred.resolve(undefined); + stream[_pendingAbortRequest] = undefined; } - assert(state === "erroring"); + } + stream[_state] = "closed"; + const writer = stream[_writer]; + if (writer !== undefined) { + writer[_closedPromise].resolve(undefined); + } + assert(stream[_pendingAbortRequest] === undefined); + assert(stream[_storedError] === undefined); +} + +/** + * @param {WritableStream} stream + * @param {any=} error + */ +function writableStreamFinishInFlightCloseWithError(stream, error) { + assert(stream[_inFlightCloseRequest] !== undefined); + stream[_inFlightCloseRequest].reject(error); + stream[_inFlightCloseRequest] = undefined; + assert(stream[_state] === "writable" || stream[_state] === "erroring"); + if (stream[_pendingAbortRequest] !== undefined) { + stream[_pendingAbortRequest].deferred.reject(error); + stream[_pendingAbortRequest] = undefined; + } + writableStreamDealWithRejection(stream, error); +} + +/** @param {WritableStream} stream */ +function writableStreamFinishInFlightWrite(stream) { + assert(stream[_inFlightWriteRequest] !== undefined); + stream[_inFlightWriteRequest].resolve(undefined); + stream[_inFlightWriteRequest] = undefined; +} + +/** + * @param {WritableStream} stream + * @param {any=} error + */ +function writableStreamFinishInFlightWriteWithError(stream, error) { + assert(stream[_inFlightWriteRequest] !== undefined); + stream[_inFlightWriteRequest].reject(error); + stream[_inFlightWriteRequest] = undefined; + assert(stream[_state] === "writable" || stream[_state] === "erroring"); + writableStreamDealWithRejection(stream, error); +} + +/** + * @param {WritableStream} stream + * @returns {boolean} + */ +function writableStreamHasOperationMarkedInFlight(stream) { + if ( + stream[_inFlightWriteRequest] === undefined && + stream[_inFlightCloseRequest] === undefined + ) { + return false; + } + return true; +} + +/** @param {WritableStream} stream */ +function writableStreamMarkCloseRequestInFlight(stream) { + assert(stream[_inFlightCloseRequest] === undefined); + assert(stream[_closeRequest] !== undefined); + stream[_inFlightCloseRequest] = stream[_closeRequest]; + stream[_closeRequest] = undefined; +} + +/** + * @template W + * @param {WritableStream<W>} stream + */ +function writableStreamMarkFirstWriteRequestInFlight(stream) { + assert(stream[_inFlightWriteRequest] === undefined); + assert(stream[_writeRequests].length); + const writeRequest = stream[_writeRequests].shift(); + stream[_inFlightWriteRequest] = writeRequest; +} + +/** @param {WritableStream} stream */ +function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { + assert(stream[_state] === "errored"); + if (stream[_closeRequest] !== undefined) { + assert(stream[_inFlightCloseRequest] === undefined); + stream[_closeRequest].reject(stream[_storedError]); + stream[_closeRequest] = undefined; + } + const writer = stream[_writer]; + if (writer !== undefined) { + writer[_closedPromise].reject(stream[_storedError]); + setPromiseIsHandledToTrue(writer[_closedPromise].promise); + } +} + +/** + * @param {WritableStream} stream + * @param {any=} reason + */ +function writableStreamStartErroring(stream, reason) { + assert(stream[_storedError] === undefined); + assert(stream[_state] === "writable"); + const controller = stream[_controller]; + assert(controller !== undefined); + stream[_state] = "erroring"; + stream[_storedError] = reason; + const writer = stream[_writer]; + if (writer !== undefined) { + writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason); + } + if ( + writableStreamHasOperationMarkedInFlight(stream) === false && + controller[_started] === true + ) { writableStreamFinishErroring(stream); } - - /** - * @template W - * @param {WritableStreamDefaultController<W>} controller - */ - function writableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { - const stream = controller[_stream]; - if (controller[_started] === false) { - return; - } - if (stream[_inFlightWriteRequest] !== undefined) { - return; - } - const state = stream[_state]; - assert(state !== "closed" && state !== "errored"); - if (state === "erroring") { - writableStreamFinishErroring(stream); - return; - } - if (controller[_queue].length === 0) { - return; - } - const value = peekQueueValue(controller); - if (value === _close) { - writableStreamDefaultControllerProcessClose(controller); +} + +/** + * @param {WritableStream} stream + * @param {boolean} backpressure + */ +function writableStreamUpdateBackpressure(stream, backpressure) { + assert(stream[_state] === "writable"); + assert(writableStreamCloseQueuedOrInFlight(stream) === false); + const writer = stream[_writer]; + if (writer !== undefined && backpressure !== stream[_backpressure]) { + if (backpressure === true) { + writer[_readyPromise] = new Deferred(); } else { - writableStreamDefaultControllerProcessWrite(controller, value); + assert(backpressure === false); + writer[_readyPromise].resolve(undefined); } } + stream[_backpressure] = backpressure; +} + +/** + * @template T + * @param {T} value + * @param {boolean} done + * @returns {IteratorResult<T>} + */ +function createIteratorResult(value, done) { + const result = ObjectCreate(ObjectPrototype); + ObjectDefineProperties(result, { + value: { value, writable: true, enumerable: true, configurable: true }, + done: { + value: done, + writable: true, + enumerable: true, + configurable: true, + }, + }); + return result; +} + +/** @type {AsyncIterator<unknown, unknown>} */ +const asyncIteratorPrototype = ObjectGetPrototypeOf(AsyncGeneratorPrototype); + +const _iteratorNext = Symbol("[[iteratorNext]]"); +const _iteratorFinished = Symbol("[[iteratorFinished]]"); + +/** @type {AsyncIterator<unknown>} */ +const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({ + /** @returns {Promise<IteratorResult<unknown>>} */ + next() { + /** @type {ReadableStreamDefaultReader} */ + const reader = this[_reader]; + function nextSteps() { + if (reader[_iteratorFinished]) { + return PromiseResolve(createIteratorResult(undefined, true)); + } - function writableStreamDefaultControllerClearAlgorithms(controller) { - controller[_writeAlgorithm] = undefined; - controller[_closeAlgorithm] = undefined; - controller[_abortAlgorithm] = undefined; - controller[_strategySizeAlgorithm] = undefined; - } + if (reader[_stream] === undefined) { + return PromiseReject( + new TypeError( + "Cannot get the next iteration result once the reader has been released.", + ), + ); + } - /** @param {WritableStreamDefaultController} controller */ - function writableStreamDefaultControllerClose(controller) { - enqueueValueWithSize(controller, _close, 0); - writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); - } + /** @type {Deferred<IteratorResult<any>>} */ + const promise = new Deferred(); + /** @type {ReadRequest} */ + const readRequest = { + chunkSteps(chunk) { + promise.resolve(createIteratorResult(chunk, false)); + }, + closeSteps() { + readableStreamDefaultReaderRelease(reader); + promise.resolve(createIteratorResult(undefined, true)); + }, + errorSteps(e) { + readableStreamDefaultReaderRelease(reader); + promise.reject(e); + }, + }; + + readableStreamDefaultReaderRead(reader, readRequest); + return PromisePrototypeThen(promise.promise, (result) => { + reader[_iteratorNext] = null; + if (result.done === true) { + reader[_iteratorFinished] = true; + return createIteratorResult(undefined, true); + } + return result; + }, (reason) => { + reader[_iteratorNext] = null; + reader[_iteratorFinished] = true; + throw reason; + }); + } + + reader[_iteratorNext] = reader[_iteratorNext] + ? PromisePrototypeThen(reader[_iteratorNext], nextSteps, nextSteps) + : nextSteps(); + return reader[_iteratorNext]; + }, /** - * @param {WritableStreamDefaultController} controller - * @param {any} error + * @param {unknown} arg + * @returns {Promise<IteratorResult<unknown>>} */ - function writableStreamDefaultControllerError(controller, error) { - const stream = controller[_stream]; - assert(stream[_state] === "writable"); - writableStreamDefaultControllerClearAlgorithms(controller); - writableStreamStartErroring(stream, error); + return(arg) { + /** @type {ReadableStreamDefaultReader} */ + const reader = this[_reader]; + const returnSteps = () => { + if (reader[_iteratorFinished]) { + return PromiseResolve(createIteratorResult(arg, true)); + } + reader[_iteratorFinished] = true; + + if (reader[_stream] === undefined) { + return PromiseResolve(createIteratorResult(undefined, true)); + } + assert(reader[_readRequests].length === 0); + if (this[_preventCancel] === false) { + const result = readableStreamReaderGenericCancel(reader, arg); + readableStreamDefaultReaderRelease(reader); + return result; + } + readableStreamDefaultReaderRelease(reader); + return PromiseResolve(createIteratorResult(undefined, true)); + }; + + const returnPromise = reader[_iteratorNext] + ? PromisePrototypeThen(reader[_iteratorNext], returnSteps, returnSteps) + : returnSteps(); + return PromisePrototypeThen( + returnPromise, + () => createIteratorResult(arg, true), + ); + }, +}, asyncIteratorPrototype); + +class ByteLengthQueuingStrategy { + /** @param {{ highWaterMark: number }} init */ + constructor(init) { + const prefix = "Failed to construct 'ByteLengthQueuingStrategy'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + init = webidl.converters.QueuingStrategyInit(init, { + prefix, + context: "Argument 1", + }); + this[webidl.brand] = webidl.brand; + this[_globalObject] = globalThis; + this[_highWaterMark] = init.highWaterMark; } - /** - * @param {WritableStreamDefaultController} controller - * @param {any} error - */ - function writableStreamDefaultControllerErrorIfNeeded(controller, error) { - if (controller[_stream][_state] === "writable") { - writableStreamDefaultControllerError(controller, error); - } + /** @returns {number} */ + get highWaterMark() { + webidl.assertBranded(this, ByteLengthQueuingStrategyPrototype); + return this[_highWaterMark]; } - /** - * @param {WritableStreamDefaultController} controller - * @returns {boolean} - */ - function writableStreamDefaultControllerGetBackpressure(controller) { - const desiredSize = writableStreamDefaultControllerGetDesiredSize( - controller, - ); - return desiredSize <= 0; + /** @returns {(chunk: ArrayBufferView) => number} */ + get size() { + webidl.assertBranded(this, ByteLengthQueuingStrategyPrototype); + initializeByteLengthSizeFunction(this[_globalObject]); + return WeakMapPrototypeGet(byteSizeFunctionWeakMap, this[_globalObject]); } - /** - * @template W - * @param {WritableStreamDefaultController<W>} controller - * @param {W} chunk - * @returns {number} - */ - function writableStreamDefaultControllerGetChunkSize(controller, chunk) { - let value; - try { - value = controller[_strategySizeAlgorithm](chunk); - } catch (e) { - writableStreamDefaultControllerErrorIfNeeded(controller, e); - return 1; - } - return value; + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + ByteLengthQueuingStrategyPrototype, + this, + ), + keys: [ + "highWaterMark", + "size", + ], + })); } +} - /** - * @param {WritableStreamDefaultController} controller - * @returns {number} - */ - function writableStreamDefaultControllerGetDesiredSize(controller) { - return controller[_strategyHWM] - controller[_queueTotalSize]; +webidl.configurePrototype(ByteLengthQueuingStrategy); +const ByteLengthQueuingStrategyPrototype = ByteLengthQueuingStrategy.prototype; + +/** @type {WeakMap<typeof globalThis, (chunk: ArrayBufferView) => number>} */ +const byteSizeFunctionWeakMap = new WeakMap(); + +function initializeByteLengthSizeFunction(globalObject) { + if (WeakMapPrototypeHas(byteSizeFunctionWeakMap, globalObject)) { + return; } + const size = (chunk) => chunk.byteLength; + WeakMapPrototypeSet(byteSizeFunctionWeakMap, globalObject, size); +} - /** @param {WritableStreamDefaultController} controller */ - function writableStreamDefaultControllerProcessClose(controller) { - const stream = controller[_stream]; - writableStreamMarkCloseRequestInFlight(stream); - dequeueValue(controller); - assert(controller[_queue].length === 0); - const sinkClosePromise = controller[_closeAlgorithm](); - writableStreamDefaultControllerClearAlgorithms(controller); - uponPromise(sinkClosePromise, () => { - writableStreamFinishInFlightClose(stream); - }, (reason) => { - writableStreamFinishInFlightCloseWithError(stream, reason); +class CountQueuingStrategy { + /** @param {{ highWaterMark: number }} init */ + constructor(init) { + const prefix = "Failed to construct 'CountQueuingStrategy'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + init = webidl.converters.QueuingStrategyInit(init, { + prefix, + context: "Argument 1", }); + this[webidl.brand] = webidl.brand; + this[_globalObject] = globalThis; + this[_highWaterMark] = init.highWaterMark; } - /** - * @template W - * @param {WritableStreamDefaultController<W>} controller - * @param {W} chunk - */ - function writableStreamDefaultControllerProcessWrite(controller, chunk) { - const stream = controller[_stream]; - writableStreamMarkFirstWriteRequestInFlight(stream); - const sinkWritePromise = controller[_writeAlgorithm](chunk, controller); - uponPromise(sinkWritePromise, () => { - writableStreamFinishInFlightWrite(stream); - const state = stream[_state]; - assert(state === "writable" || state === "erroring"); - dequeueValue(controller); - if ( - writableStreamCloseQueuedOrInFlight(stream) === false && - state === "writable" - ) { - const backpressure = writableStreamDefaultControllerGetBackpressure( - controller, - ); - writableStreamUpdateBackpressure(stream, backpressure); - } - writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); - }, (reason) => { - if (stream[_state] === "writable") { - writableStreamDefaultControllerClearAlgorithms(controller); - } - writableStreamFinishInFlightWriteWithError(stream, reason); - }); + /** @returns {number} */ + get highWaterMark() { + webidl.assertBranded(this, CountQueuingStrategyPrototype); + return this[_highWaterMark]; } - /** - * @template W - * @param {WritableStreamDefaultController<W>} controller - * @param {W} chunk - * @param {number} chunkSize - */ - function writableStreamDefaultControllerWrite(controller, chunk, chunkSize) { - try { - enqueueValueWithSize(controller, chunk, chunkSize); - } catch (e) { - writableStreamDefaultControllerErrorIfNeeded(controller, e); - return; + /** @returns {(chunk: any) => 1} */ + get size() { + webidl.assertBranded(this, CountQueuingStrategyPrototype); + initializeCountSizeFunction(this[_globalObject]); + return WeakMapPrototypeGet(countSizeFunctionWeakMap, this[_globalObject]); + } + + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + CountQueuingStrategyPrototype, + this, + ), + keys: [ + "highWaterMark", + "size", + ], + })); + } +} + +webidl.configurePrototype(CountQueuingStrategy); +const CountQueuingStrategyPrototype = CountQueuingStrategy.prototype; + +/** @type {WeakMap<typeof globalThis, () => 1>} */ +const countSizeFunctionWeakMap = new WeakMap(); + +/** @param {typeof globalThis} globalObject */ +function initializeCountSizeFunction(globalObject) { + if (WeakMapPrototypeHas(countSizeFunctionWeakMap, globalObject)) { + return; + } + const size = () => 1; + WeakMapPrototypeSet(countSizeFunctionWeakMap, globalObject, size); +} + +const _resourceBacking = Symbol("[[resourceBacking]]"); +// This distinction exists to prevent unrefable streams being used in +// regular fast streams that are unaware of refability +const _resourceBackingUnrefable = Symbol("[[resourceBackingUnrefable]]"); +/** @template R */ +class ReadableStream { + /** @type {ReadableStreamDefaultController | ReadableByteStreamController} */ + [_controller]; + /** @type {boolean} */ + [_detached]; + /** @type {boolean} */ + [_disturbed]; + /** @type {ReadableStreamDefaultReader | ReadableStreamBYOBReader} */ + [_reader]; + /** @type {"readable" | "closed" | "errored"} */ + [_state]; + /** @type {any} */ + [_storedError]; + /** @type {{ rid: number, autoClose: boolean } | null} */ + [_resourceBacking] = null; + + /** + * @param {UnderlyingSource<R>=} underlyingSource + * @param {QueuingStrategy<R>=} strategy + */ + constructor(underlyingSource = undefined, strategy = undefined) { + const prefix = "Failed to construct 'ReadableStream'"; + if (underlyingSource !== undefined) { + underlyingSource = webidl.converters.object(underlyingSource, { + prefix, + context: "Argument 1", + }); + } else { + underlyingSource = null; } - const stream = controller[_stream]; - if ( - writableStreamCloseQueuedOrInFlight(stream) === false && - stream[_state] === "writable" - ) { - const backpressure = writableStreamDefaultControllerGetBackpressure( - controller, + if (strategy !== undefined) { + strategy = webidl.converters.QueuingStrategy(strategy, { + prefix, + context: "Argument 2", + }); + } else { + strategy = {}; + } + this[webidl.brand] = webidl.brand; + let underlyingSourceDict = {}; + if (underlyingSource !== undefined) { + underlyingSourceDict = webidl.converters.UnderlyingSource( + underlyingSource, + { prefix, context: "underlyingSource" }, + ); + } + initializeReadableStream(this); + if (underlyingSourceDict.type === "bytes") { + if (strategy.size !== undefined) { + throw new RangeError( + `${prefix}: When underlying source is "bytes", strategy.size must be undefined.`, + ); + } + const highWaterMark = extractHighWaterMark(strategy, 0); + setUpReadableByteStreamControllerFromUnderlyingSource( + // @ts-ignore cannot easily assert this is ReadableStream<ArrayBuffer> + this, + underlyingSource, + underlyingSourceDict, + highWaterMark, + ); + } else { + assert(!(ReflectHas(underlyingSourceDict, "type"))); + const sizeAlgorithm = extractSizeAlgorithm(strategy); + const highWaterMark = extractHighWaterMark(strategy, 1); + setUpReadableStreamDefaultControllerFromUnderlyingSource( + this, + underlyingSource, + underlyingSourceDict, + highWaterMark, + sizeAlgorithm, ); - writableStreamUpdateBackpressure(stream, backpressure); } - writableStreamDefaultControllerAdvanceQueueIfNeeded(controller); - } - - /** - * @param {WritableStreamDefaultWriter} writer - * @param {any=} reason - * @returns {Promise<void>} - */ - function writableStreamDefaultWriterAbort(writer, reason) { - const stream = writer[_stream]; - assert(stream !== undefined); - return writableStreamAbort(stream, reason); } - /** - * @param {WritableStreamDefaultWriter} writer - * @returns {Promise<void>} - */ - function writableStreamDefaultWriterClose(writer) { - const stream = writer[_stream]; - assert(stream !== undefined); - return writableStreamClose(stream); + /** @returns {boolean} */ + get locked() { + webidl.assertBranded(this, ReadableStreamPrototype); + return isReadableStreamLocked(this); } /** - * @param {WritableStreamDefaultWriter} writer + * @param {any=} reason * @returns {Promise<void>} */ - function writableStreamDefaultWriterCloseWithErrorPropagation(writer) { - const stream = writer[_stream]; - assert(stream !== undefined); - const state = stream[_state]; - if ( - writableStreamCloseQueuedOrInFlight(stream) === true || state === "closed" - ) { - return resolvePromiseWith(undefined); + cancel(reason = undefined) { + try { + webidl.assertBranded(this, ReadableStreamPrototype); + if (reason !== undefined) { + reason = webidl.converters.any(reason); + } + } catch (err) { + return PromiseReject(err); } - if (state === "errored") { - return PromiseReject(stream[_storedError]); + if (isReadableStreamLocked(this)) { + return PromiseReject( + new TypeError("Cannot cancel a locked ReadableStream."), + ); } - assert(state === "writable" || state === "erroring"); - return writableStreamDefaultWriterClose(writer); + return readableStreamCancel(this, reason); } /** - * @param {WritableStreamDefaultWriter} writer - * @param {any=} error + * @param {ReadableStreamGetReaderOptions=} options + * @returns {ReadableStreamDefaultReader<R> | ReadableStreamBYOBReader} */ - function writableStreamDefaultWriterEnsureClosedPromiseRejected( - writer, - error, - ) { - if (writer[_closedPromise].state === "pending") { - writer[_closedPromise].reject(error); + getReader(options = undefined) { + webidl.assertBranded(this, ReadableStreamPrototype); + const prefix = "Failed to execute 'getReader' on 'ReadableStream'"; + if (options !== undefined) { + options = webidl.converters.ReadableStreamGetReaderOptions(options, { + prefix, + context: "Argument 1", + }); } else { - writer[_closedPromise] = new Deferred(); - writer[_closedPromise].reject(error); + options = {}; + } + if (options.mode === undefined) { + return acquireReadableStreamDefaultReader(this); + } else { + assert(options.mode === "byob"); + return acquireReadableStreamBYOBReader(this); } - setPromiseIsHandledToTrue(writer[_closedPromise].promise); } /** - * @param {WritableStreamDefaultWriter} writer - * @param {any=} error - */ - function writableStreamDefaultWriterEnsureReadyPromiseRejected( - writer, - error, - ) { - if (writer[_readyPromise].state === "pending") { - writer[_readyPromise].reject(error); - } else { - writer[_readyPromise] = new Deferred(); - writer[_readyPromise].reject(error); - } - setPromiseIsHandledToTrue(writer[_readyPromise].promise); + * @template T + * @param {{ readable: ReadableStream<T>, writable: WritableStream<R> }} transform + * @param {PipeOptions=} options + * @returns {ReadableStream<T>} + */ + pipeThrough(transform, options = {}) { + webidl.assertBranded(this, ReadableStreamPrototype); + const prefix = "Failed to execute 'pipeThrough' on 'ReadableStream'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + transform = webidl.converters.ReadableWritablePair(transform, { + prefix, + context: "Argument 1", + }); + options = webidl.converters.StreamPipeOptions(options, { + prefix, + context: "Argument 2", + }); + const { readable, writable } = transform; + const { preventClose, preventAbort, preventCancel, signal } = options; + if (isReadableStreamLocked(this)) { + throw new TypeError("ReadableStream is already locked."); + } + if (isWritableStreamLocked(writable)) { + throw new TypeError("Target WritableStream is already locked."); + } + const promise = readableStreamPipeTo( + this, + writable, + preventClose, + preventAbort, + preventCancel, + signal, + ); + setPromiseIsHandledToTrue(promise); + return readable; } /** - * @param {WritableStreamDefaultWriter} writer - * @returns {number | null} + * @param {WritableStream<R>} destination + * @param {PipeOptions=} options + * @returns {Promise<void>} */ - function writableStreamDefaultWriterGetDesiredSize(writer) { - const stream = writer[_stream]; - const state = stream[_state]; - if (state === "errored" || state === "erroring") { - return null; + pipeTo(destination, options = {}) { + try { + webidl.assertBranded(this, ReadableStreamPrototype); + const prefix = "Failed to execute 'pipeTo' on 'ReadableStream'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + destination = webidl.converters.WritableStream(destination, { + prefix, + context: "Argument 1", + }); + options = webidl.converters.StreamPipeOptions(options, { + prefix, + context: "Argument 2", + }); + } catch (err) { + return PromiseReject(err); } - if (state === "closed") { - return 0; + const { preventClose, preventAbort, preventCancel, signal } = options; + if (isReadableStreamLocked(this)) { + return PromiseReject( + new TypeError("ReadableStream is already locked."), + ); + } + if (isWritableStreamLocked(destination)) { + return PromiseReject( + new TypeError("destination WritableStream is already locked."), + ); } - return writableStreamDefaultControllerGetDesiredSize(stream[_controller]); + return readableStreamPipeTo( + this, + destination, + preventClose, + preventAbort, + preventCancel, + signal, + ); } - /** @param {WritableStreamDefaultWriter} writer */ - function writableStreamDefaultWriterRelease(writer) { - const stream = writer[_stream]; - assert(stream !== undefined); - assert(stream[_writer] === writer); - const releasedError = new TypeError( - "The writer has already been released.", - ); - writableStreamDefaultWriterEnsureReadyPromiseRejected( - writer, - releasedError, - ); - writableStreamDefaultWriterEnsureClosedPromiseRejected( - writer, - releasedError, - ); - stream[_writer] = undefined; - writer[_stream] = undefined; + /** @returns {[ReadableStream<R>, ReadableStream<R>]} */ + tee() { + webidl.assertBranded(this, ReadableStreamPrototype); + return readableStreamTee(this, false); } + // TODO(lucacasonato): should be moved to webidl crate /** - * @template W - * @param {WritableStreamDefaultWriter<W>} writer - * @param {W} chunk - * @returns {Promise<void>} + * @param {ReadableStreamIteratorOptions=} options + * @returns {AsyncIterableIterator<R>} */ - function writableStreamDefaultWriterWrite(writer, chunk) { - const stream = writer[_stream]; - assert(stream !== undefined); - const controller = stream[_controller]; - const chunkSize = writableStreamDefaultControllerGetChunkSize( - controller, - chunk, - ); - if (stream !== writer[_stream]) { - return PromiseReject(new TypeError("Writer's stream is unexpected.")); - } - const state = stream[_state]; - if (state === "errored") { - return PromiseReject(stream[_storedError]); + values(options = {}) { + webidl.assertBranded(this, ReadableStreamPrototype); + const prefix = "Failed to execute 'values' on 'ReadableStream'"; + options = webidl.converters.ReadableStreamIteratorOptions(options, { + prefix, + context: "Argument 1", + }); + /** @type {AsyncIterableIterator<R>} */ + const iterator = ObjectCreate(readableStreamAsyncIteratorPrototype); + const reader = acquireReadableStreamDefaultReader(this); + iterator[_reader] = reader; + iterator[_preventCancel] = options.preventCancel; + return iterator; + } + + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return `${this.constructor.name} ${inspect({ locked: this.locked })}`; + } +} + +// TODO(lucacasonato): should be moved to webidl crate +ReadableStream.prototype[SymbolAsyncIterator] = ReadableStream.prototype.values; +ObjectDefineProperty(ReadableStream.prototype, SymbolAsyncIterator, { + writable: true, + enumerable: false, + configurable: true, +}); + +webidl.configurePrototype(ReadableStream); +const ReadableStreamPrototype = ReadableStream.prototype; + +function errorReadableStream(stream, e) { + readableStreamDefaultControllerError(stream[_controller], e); +} + +/** @template R */ +class ReadableStreamDefaultReader { + /** @type {Deferred<void>} */ + [_closedPromise]; + /** @type {ReadableStream<R> | undefined} */ + [_stream]; + /** @type {ReadRequest[]} */ + [_readRequests]; + + /** @param {ReadableStream<R>} stream */ + constructor(stream) { + const prefix = "Failed to construct 'ReadableStreamDefaultReader'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + stream = webidl.converters.ReadableStream(stream, { + prefix, + context: "Argument 1", + }); + this[webidl.brand] = webidl.brand; + setUpReadableStreamDefaultReader(this, stream); + } + + /** @returns {Promise<ReadableStreamReadResult<R>>} */ + read() { + try { + webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); + } catch (err) { + return PromiseReject(err); } - if ( - writableStreamCloseQueuedOrInFlight(stream) === true || state === "closed" - ) { + if (this[_stream] === undefined) { return PromiseReject( - new TypeError("The stream is closing or is closed."), + new TypeError("Reader has no associated stream."), ); } - if (state === "erroring") { - return PromiseReject(stream[_storedError]); - } - assert(state === "writable"); - const promise = writableStreamAddWriteRequest(stream); - writableStreamDefaultControllerWrite(controller, chunk, chunkSize); - return promise; + /** @type {Deferred<ReadableStreamReadResult<R>>} */ + const promise = new Deferred(); + /** @type {ReadRequest<R>} */ + const readRequest = { + chunkSteps(chunk) { + promise.resolve({ value: chunk, done: false }); + }, + closeSteps() { + promise.resolve({ value: undefined, done: true }); + }, + errorSteps(e) { + promise.reject(e); + }, + }; + readableStreamDefaultReaderRead(this, readRequest); + return promise.promise; } - /** @param {WritableStream} stream */ - function writableStreamFinishErroring(stream) { - assert(stream[_state] === "erroring"); - assert(writableStreamHasOperationMarkedInFlight(stream) === false); - stream[_state] = "errored"; - stream[_controller][_errorSteps](); - const storedError = stream[_storedError]; - const writeRequests = stream[_writeRequests]; - for (let i = 0; i < writeRequests.length; ++i) { - const writeRequest = writeRequests[i]; - writeRequest.reject(storedError); - } - stream[_writeRequests] = []; - if (stream[_pendingAbortRequest] === undefined) { - writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); - return; - } - const abortRequest = stream[_pendingAbortRequest]; - stream[_pendingAbortRequest] = undefined; - if (abortRequest.wasAlreadyErroring === true) { - abortRequest.deferred.reject(storedError); - writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + /** @returns {void} */ + releaseLock() { + webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); + if (this[_stream] === undefined) { return; } - const promise = stream[_controller][_abortSteps](abortRequest.reason); - uponPromise(promise, () => { - abortRequest.deferred.resolve(undefined); - writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); - }, (reason) => { - abortRequest.deferred.reject(reason); - writableStreamRejectCloseAndClosedPromiseIfNeeded(stream); - }); + readableStreamDefaultReaderRelease(this); } - /** @param {WritableStream} stream */ - function writableStreamFinishInFlightClose(stream) { - assert(stream[_inFlightCloseRequest] !== undefined); - stream[_inFlightCloseRequest].resolve(undefined); - stream[_inFlightCloseRequest] = undefined; - const state = stream[_state]; - assert(state === "writable" || state === "erroring"); - if (state === "erroring") { - stream[_storedError] = undefined; - if (stream[_pendingAbortRequest] !== undefined) { - stream[_pendingAbortRequest].deferred.resolve(undefined); - stream[_pendingAbortRequest] = undefined; - } - } - stream[_state] = "closed"; - const writer = stream[_writer]; - if (writer !== undefined) { - writer[_closedPromise].resolve(undefined); + get closed() { + try { + webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); + } catch (err) { + return PromiseReject(err); } - assert(stream[_pendingAbortRequest] === undefined); - assert(stream[_storedError] === undefined); + return this[_closedPromise].promise; } /** - * @param {WritableStream} stream - * @param {any=} error + * @param {any} reason + * @returns {Promise<void>} */ - function writableStreamFinishInFlightCloseWithError(stream, error) { - assert(stream[_inFlightCloseRequest] !== undefined); - stream[_inFlightCloseRequest].reject(error); - stream[_inFlightCloseRequest] = undefined; - assert(stream[_state] === "writable" || stream[_state] === "erroring"); - if (stream[_pendingAbortRequest] !== undefined) { - stream[_pendingAbortRequest].deferred.reject(error); - stream[_pendingAbortRequest] = undefined; + cancel(reason = undefined) { + try { + webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); + if (reason !== undefined) { + reason = webidl.converters.any(reason); + } + } catch (err) { + return PromiseReject(err); } - writableStreamDealWithRejection(stream, error); - } - /** @param {WritableStream} stream */ - function writableStreamFinishInFlightWrite(stream) { - assert(stream[_inFlightWriteRequest] !== undefined); - stream[_inFlightWriteRequest].resolve(undefined); - stream[_inFlightWriteRequest] = undefined; + if (this[_stream] === undefined) { + return PromiseReject( + new TypeError("Reader has no associated stream."), + ); + } + return readableStreamReaderGenericCancel(this, reason); } - /** - * @param {WritableStream} stream - * @param {any=} error - */ - function writableStreamFinishInFlightWriteWithError(stream, error) { - assert(stream[_inFlightWriteRequest] !== undefined); - stream[_inFlightWriteRequest].reject(error); - stream[_inFlightWriteRequest] = undefined; - assert(stream[_state] === "writable" || stream[_state] === "erroring"); - writableStreamDealWithRejection(stream, error); + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return `${this.constructor.name} ${inspect({ closed: this.closed })}`; } +} - /** - * @param {WritableStream} stream - * @returns {boolean} - */ - function writableStreamHasOperationMarkedInFlight(stream) { - if ( - stream[_inFlightWriteRequest] === undefined && - stream[_inFlightCloseRequest] === undefined - ) { - return false; - } - return true; - } +webidl.configurePrototype(ReadableStreamDefaultReader); +const ReadableStreamDefaultReaderPrototype = + ReadableStreamDefaultReader.prototype; - /** @param {WritableStream} stream */ - function writableStreamMarkCloseRequestInFlight(stream) { - assert(stream[_inFlightCloseRequest] === undefined); - assert(stream[_closeRequest] !== undefined); - stream[_inFlightCloseRequest] = stream[_closeRequest]; - stream[_closeRequest] = undefined; +/** @template R */ +class ReadableStreamBYOBReader { + /** @type {Deferred<void>} */ + [_closedPromise]; + /** @type {ReadableStream<R> | undefined} */ + [_stream]; + /** @type {ReadIntoRequest[]} */ + [_readIntoRequests]; + + /** @param {ReadableStream<R>} stream */ + constructor(stream) { + const prefix = "Failed to construct 'ReadableStreamBYOBReader'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + stream = webidl.converters.ReadableStream(stream, { + prefix, + context: "Argument 1", + }); + this[webidl.brand] = webidl.brand; + setUpReadableStreamBYOBReader(this, stream); } /** - * @template W - * @param {WritableStream<W>} stream + * @param {ArrayBufferView} view + * @returns {Promise<ReadableStreamBYOBReadResult>} */ - function writableStreamMarkFirstWriteRequestInFlight(stream) { - assert(stream[_inFlightWriteRequest] === undefined); - assert(stream[_writeRequests].length); - const writeRequest = stream[_writeRequests].shift(); - stream[_inFlightWriteRequest] = writeRequest; - } + read(view) { + try { + webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); + const prefix = "Failed to execute 'read' on 'ReadableStreamBYOBReader'"; + view = webidl.converters.ArrayBufferView(view, { + prefix, + context: "Argument 1", + }); + } catch (err) { + return PromiseReject(err); + } - /** @param {WritableStream} stream */ - function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { - assert(stream[_state] === "errored"); - if (stream[_closeRequest] !== undefined) { - assert(stream[_inFlightCloseRequest] === undefined); - stream[_closeRequest].reject(stream[_storedError]); - stream[_closeRequest] = undefined; + if (view.byteLength === 0) { + return PromiseReject( + new TypeError("view must have non-zero byteLength"), + ); } - const writer = stream[_writer]; - if (writer !== undefined) { - writer[_closedPromise].reject(stream[_storedError]); - setPromiseIsHandledToTrue(writer[_closedPromise].promise); + if (view.buffer.byteLength === 0) { + return PromiseReject( + new TypeError("view's buffer must have non-zero byteLength"), + ); + } + if (isDetachedBuffer(view.buffer)) { + return PromiseReject( + new TypeError("view's buffer has been detached"), + ); } + if (this[_stream] === undefined) { + return PromiseReject( + new TypeError("Reader has no associated stream."), + ); + } + /** @type {Deferred<ReadableStreamBYOBReadResult>} */ + const promise = new Deferred(); + /** @type {ReadIntoRequest} */ + const readIntoRequest = { + chunkSteps(chunk) { + promise.resolve({ value: chunk, done: false }); + }, + closeSteps(chunk) { + promise.resolve({ value: chunk, done: true }); + }, + errorSteps(e) { + promise.reject(e); + }, + }; + readableStreamBYOBReaderRead(this, view, readIntoRequest); + return promise.promise; } - /** - * @param {WritableStream} stream - * @param {any=} reason - */ - function writableStreamStartErroring(stream, reason) { - assert(stream[_storedError] === undefined); - assert(stream[_state] === "writable"); - const controller = stream[_controller]; - assert(controller !== undefined); - stream[_state] = "erroring"; - stream[_storedError] = reason; - const writer = stream[_writer]; - if (writer !== undefined) { - writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason); - } - if ( - writableStreamHasOperationMarkedInFlight(stream) === false && - controller[_started] === true - ) { - writableStreamFinishErroring(stream); + /** @returns {void} */ + releaseLock() { + webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); + if (this[_stream] === undefined) { + return; } + readableStreamBYOBReaderRelease(this); } - /** - * @param {WritableStream} stream - * @param {boolean} backpressure - */ - function writableStreamUpdateBackpressure(stream, backpressure) { - assert(stream[_state] === "writable"); - assert(writableStreamCloseQueuedOrInFlight(stream) === false); - const writer = stream[_writer]; - if (writer !== undefined && backpressure !== stream[_backpressure]) { - if (backpressure === true) { - writer[_readyPromise] = new Deferred(); - } else { - assert(backpressure === false); - writer[_readyPromise].resolve(undefined); - } + get closed() { + try { + webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); + } catch (err) { + return PromiseReject(err); } - stream[_backpressure] = backpressure; + return this[_closedPromise].promise; } /** - * @template T - * @param {T} value - * @param {boolean} done - * @returns {IteratorResult<T>} + * @param {any} reason + * @returns {Promise<void>} */ - function createIteratorResult(value, done) { - const result = ObjectCreate(ObjectPrototype); - ObjectDefineProperties(result, { - value: { value, writable: true, enumerable: true, configurable: true }, - done: { - value: done, - writable: true, - enumerable: true, - configurable: true, - }, - }); - return result; - } - - /** @type {AsyncIterator<unknown, unknown>} */ - const asyncIteratorPrototype = ObjectGetPrototypeOf(AsyncGeneratorPrototype); - - const _iteratorNext = Symbol("[[iteratorNext]]"); - const _iteratorFinished = Symbol("[[iteratorFinished]]"); - - /** @type {AsyncIterator<unknown>} */ - const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({ - /** @returns {Promise<IteratorResult<unknown>>} */ - next() { - /** @type {ReadableStreamDefaultReader} */ - const reader = this[_reader]; - function nextSteps() { - if (reader[_iteratorFinished]) { - return PromiseResolve(createIteratorResult(undefined, true)); - } - - if (reader[_stream] === undefined) { - return PromiseReject( - new TypeError( - "Cannot get the next iteration result once the reader has been released.", - ), - ); - } - - /** @type {Deferred<IteratorResult<any>>} */ - const promise = new Deferred(); - /** @type {ReadRequest} */ - const readRequest = { - chunkSteps(chunk) { - promise.resolve(createIteratorResult(chunk, false)); - }, - closeSteps() { - readableStreamDefaultReaderRelease(reader); - promise.resolve(createIteratorResult(undefined, true)); - }, - errorSteps(e) { - readableStreamDefaultReaderRelease(reader); - promise.reject(e); - }, - }; - - readableStreamDefaultReaderRead(reader, readRequest); - return PromisePrototypeThen(promise.promise, (result) => { - reader[_iteratorNext] = null; - if (result.done === true) { - reader[_iteratorFinished] = true; - return createIteratorResult(undefined, true); - } - return result; - }, (reason) => { - reader[_iteratorNext] = null; - reader[_iteratorFinished] = true; - throw reason; - }); + cancel(reason = undefined) { + try { + webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); + if (reason !== undefined) { + reason = webidl.converters.any(reason); } - - reader[_iteratorNext] = reader[_iteratorNext] - ? PromisePrototypeThen(reader[_iteratorNext], nextSteps, nextSteps) - : nextSteps(); - - return reader[_iteratorNext]; - }, - /** - * @param {unknown} arg - * @returns {Promise<IteratorResult<unknown>>} - */ - return(arg) { - /** @type {ReadableStreamDefaultReader} */ - const reader = this[_reader]; - const returnSteps = () => { - if (reader[_iteratorFinished]) { - return PromiseResolve(createIteratorResult(arg, true)); - } - reader[_iteratorFinished] = true; - - if (reader[_stream] === undefined) { - return PromiseResolve(createIteratorResult(undefined, true)); - } - assert(reader[_readRequests].length === 0); - if (this[_preventCancel] === false) { - const result = readableStreamReaderGenericCancel(reader, arg); - readableStreamDefaultReaderRelease(reader); - return result; - } - readableStreamDefaultReaderRelease(reader); - return PromiseResolve(createIteratorResult(undefined, true)); - }; - - const returnPromise = reader[_iteratorNext] - ? PromisePrototypeThen(reader[_iteratorNext], returnSteps, returnSteps) - : returnSteps(); - return PromisePrototypeThen( - returnPromise, - () => createIteratorResult(arg, true), - ); - }, - }, asyncIteratorPrototype); - - class ByteLengthQueuingStrategy { - /** @param {{ highWaterMark: number }} init */ - constructor(init) { - const prefix = "Failed to construct 'ByteLengthQueuingStrategy'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - init = webidl.converters.QueuingStrategyInit(init, { - prefix, - context: "Argument 1", - }); - this[webidl.brand] = webidl.brand; - this[_globalObject] = window; - this[_highWaterMark] = init.highWaterMark; + } catch (err) { + return PromiseReject(err); } - /** @returns {number} */ - get highWaterMark() { - webidl.assertBranded(this, ByteLengthQueuingStrategyPrototype); - return this[_highWaterMark]; - } - - /** @returns {(chunk: ArrayBufferView) => number} */ - get size() { - webidl.assertBranded(this, ByteLengthQueuingStrategyPrototype); - initializeByteLengthSizeFunction(this[_globalObject]); - return WeakMapPrototypeGet(byteSizeFunctionWeakMap, this[_globalObject]); + if (this[_stream] === undefined) { + return PromiseReject( + new TypeError("Reader has no associated stream."), + ); } + return readableStreamReaderGenericCancel(this, reason); + } - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - ByteLengthQueuingStrategyPrototype, - this, - ), - keys: [ - "highWaterMark", - "size", - ], - })); - } + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return `${this.constructor.name} ${inspect({ closed: this.closed })}`; } +} - webidl.configurePrototype(ByteLengthQueuingStrategy); - const ByteLengthQueuingStrategyPrototype = - ByteLengthQueuingStrategy.prototype; +webidl.configurePrototype(ReadableStreamBYOBReader); +const ReadableStreamBYOBReaderPrototype = ReadableStreamBYOBReader.prototype; - /** @type {WeakMap<typeof globalThis, (chunk: ArrayBufferView) => number>} */ - const byteSizeFunctionWeakMap = new WeakMap(); +class ReadableStreamBYOBRequest { + /** @type {ReadableByteStreamController} */ + [_controller]; + /** @type {ArrayBufferView | null} */ + [_view]; - function initializeByteLengthSizeFunction(globalObject) { - if (WeakMapPrototypeHas(byteSizeFunctionWeakMap, globalObject)) { - return; - } - const size = (chunk) => chunk.byteLength; - WeakMapPrototypeSet(byteSizeFunctionWeakMap, globalObject, size); + /** @returns {ArrayBufferView | null} */ + get view() { + webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype); + return this[_view]; } - class CountQueuingStrategy { - /** @param {{ highWaterMark: number }} init */ - constructor(init) { - const prefix = "Failed to construct 'CountQueuingStrategy'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - init = webidl.converters.QueuingStrategyInit(init, { - prefix, - context: "Argument 1", - }); - this[webidl.brand] = webidl.brand; - this[_globalObject] = window; - this[_highWaterMark] = init.highWaterMark; - } + constructor() { + webidl.illegalConstructor(); + } - /** @returns {number} */ - get highWaterMark() { - webidl.assertBranded(this, CountQueuingStrategyPrototype); - return this[_highWaterMark]; - } + respond(bytesWritten) { + webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype); + const prefix = "Failed to execute 'respond' on 'ReadableStreamBYOBRequest'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + bytesWritten = webidl.converters["unsigned long long"](bytesWritten, { + enforceRange: true, + prefix, + context: "Argument 1", + }); - /** @returns {(chunk: any) => 1} */ - get size() { - webidl.assertBranded(this, CountQueuingStrategyPrototype); - initializeCountSizeFunction(this[_globalObject]); - return WeakMapPrototypeGet(countSizeFunctionWeakMap, this[_globalObject]); + if (this[_controller] === undefined) { + throw new TypeError("This BYOB request has been invalidated"); } - - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - CountQueuingStrategyPrototype, - this, - ), - keys: [ - "highWaterMark", - "size", - ], - })); + if (isDetachedBuffer(this[_view].buffer)) { + throw new TypeError( + "The BYOB request's buffer has been detached and so cannot be used as a response", + ); } + assert(this[_view].byteLength > 0); + assert(this[_view].buffer.byteLength > 0); + readableByteStreamControllerRespond(this[_controller], bytesWritten); } - webidl.configurePrototype(CountQueuingStrategy); - const CountQueuingStrategyPrototype = CountQueuingStrategy.prototype; - - /** @type {WeakMap<typeof globalThis, () => 1>} */ - const countSizeFunctionWeakMap = new WeakMap(); - - /** @param {typeof globalThis} globalObject */ - function initializeCountSizeFunction(globalObject) { - if (WeakMapPrototypeHas(countSizeFunctionWeakMap, globalObject)) { - return; - } - const size = () => 1; - WeakMapPrototypeSet(countSizeFunctionWeakMap, globalObject, size); - } - - const _resourceBacking = Symbol("[[resourceBacking]]"); - // This distinction exists to prevent unrefable streams being used in - // regular fast streams that are unaware of refability - const _resourceBackingUnrefable = Symbol("[[resourceBackingUnrefable]]"); - /** @template R */ - class ReadableStream { - /** @type {ReadableStreamDefaultController | ReadableByteStreamController} */ - [_controller]; - /** @type {boolean} */ - [_detached]; - /** @type {boolean} */ - [_disturbed]; - /** @type {ReadableStreamDefaultReader | ReadableStreamBYOBReader} */ - [_reader]; - /** @type {"readable" | "closed" | "errored"} */ - [_state]; - /** @type {any} */ - [_storedError]; - /** @type {{ rid: number, autoClose: boolean } | null} */ - [_resourceBacking] = null; - - /** - * @param {UnderlyingSource<R>=} underlyingSource - * @param {QueuingStrategy<R>=} strategy - */ - constructor(underlyingSource = undefined, strategy = undefined) { - const prefix = "Failed to construct 'ReadableStream'"; - if (underlyingSource !== undefined) { - underlyingSource = webidl.converters.object(underlyingSource, { - prefix, - context: "Argument 1", - }); - } else { - underlyingSource = null; - } - if (strategy !== undefined) { - strategy = webidl.converters.QueuingStrategy(strategy, { - prefix, - context: "Argument 2", - }); - } else { - strategy = {}; - } - this[webidl.brand] = webidl.brand; - let underlyingSourceDict = {}; - if (underlyingSource !== undefined) { - underlyingSourceDict = webidl.converters.UnderlyingSource( - underlyingSource, - { prefix, context: "underlyingSource" }, - ); - } - initializeReadableStream(this); - if (underlyingSourceDict.type === "bytes") { - if (strategy.size !== undefined) { - throw new RangeError( - `${prefix}: When underlying source is "bytes", strategy.size must be undefined.`, - ); - } - const highWaterMark = extractHighWaterMark(strategy, 0); - setUpReadableByteStreamControllerFromUnderlyingSource( - // @ts-ignore cannot easily assert this is ReadableStream<ArrayBuffer> - this, - underlyingSource, - underlyingSourceDict, - highWaterMark, - ); - } else { - assert(!(ReflectHas(underlyingSourceDict, "type"))); - const sizeAlgorithm = extractSizeAlgorithm(strategy); - const highWaterMark = extractHighWaterMark(strategy, 1); - setUpReadableStreamDefaultControllerFromUnderlyingSource( - this, - underlyingSource, - underlyingSourceDict, - highWaterMark, - sizeAlgorithm, - ); - } - } + respondWithNewView(view) { + webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype); + const prefix = + "Failed to execute 'respondWithNewView' on 'ReadableStreamBYOBRequest'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + view = webidl.converters.ArrayBufferView(view, { + prefix, + context: "Argument 1", + }); - /** @returns {boolean} */ - get locked() { - webidl.assertBranded(this, ReadableStreamPrototype); - return isReadableStreamLocked(this); + if (this[_controller] === undefined) { + throw new TypeError("This BYOB request has been invalidated"); } - - /** - * @param {any=} reason - * @returns {Promise<void>} - */ - cancel(reason = undefined) { - try { - webidl.assertBranded(this, ReadableStreamPrototype); - if (reason !== undefined) { - reason = webidl.converters.any(reason); - } - } catch (err) { - return PromiseReject(err); - } - if (isReadableStreamLocked(this)) { - return PromiseReject( - new TypeError("Cannot cancel a locked ReadableStream."), - ); - } - return readableStreamCancel(this, reason); + if (isDetachedBuffer(view.buffer)) { + throw new TypeError( + "The given view's buffer has been detached and so cannot be used as a response", + ); } - - /** - * @param {ReadableStreamGetReaderOptions=} options - * @returns {ReadableStreamDefaultReader<R> | ReadableStreamBYOBReader} - */ - getReader(options = undefined) { - webidl.assertBranded(this, ReadableStreamPrototype); - const prefix = "Failed to execute 'getReader' on 'ReadableStream'"; - if (options !== undefined) { - options = webidl.converters.ReadableStreamGetReaderOptions(options, { - prefix, - context: "Argument 1", - }); - } else { - options = {}; - } - if (options.mode === undefined) { - return acquireReadableStreamDefaultReader(this); - } else { - assert(options.mode === "byob"); - return acquireReadableStreamBYOBReader(this); - } + readableByteStreamControllerRespondWithNewView(this[_controller], view); + } +} + +webidl.configurePrototype(ReadableStreamBYOBRequest); +const ReadableStreamBYOBRequestPrototype = ReadableStreamBYOBRequest.prototype; + +class ReadableByteStreamController { + /** @type {number | undefined} */ + [_autoAllocateChunkSize]; + /** @type {ReadableStreamBYOBRequest | null} */ + [_byobRequest]; + /** @type {(reason: any) => Promise<void>} */ + [_cancelAlgorithm]; + /** @type {boolean} */ + [_closeRequested]; + /** @type {boolean} */ + [_pullAgain]; + /** @type {(controller: this) => Promise<void>} */ + [_pullAlgorithm]; + /** @type {boolean} */ + [_pulling]; + /** @type {PullIntoDescriptor[]} */ + [_pendingPullIntos]; + /** @type {ReadableByteStreamQueueEntry[]} */ + [_queue]; + /** @type {number} */ + [_queueTotalSize]; + /** @type {boolean} */ + [_started]; + /** @type {number} */ + [_strategyHWM]; + /** @type {ReadableStream<ArrayBuffer>} */ + [_stream]; + + constructor() { + webidl.illegalConstructor(); + } + + /** @returns {ReadableStreamBYOBRequest | null} */ + get byobRequest() { + webidl.assertBranded(this, ReadableByteStreamControllerPrototype); + return readableByteStreamControllerGetBYOBRequest(this); + } + + /** @returns {number | null} */ + get desiredSize() { + webidl.assertBranded(this, ReadableByteStreamControllerPrototype); + return readableByteStreamControllerGetDesiredSize(this); + } + + /** @returns {void} */ + close() { + webidl.assertBranded(this, ReadableByteStreamControllerPrototype); + if (this[_closeRequested] === true) { + throw new TypeError("Closed already requested."); + } + if (this[_stream][_state] !== "readable") { + throw new TypeError( + "ReadableByteStreamController's stream is not in a readable state.", + ); } + readableByteStreamControllerClose(this); + } - /** - * @template T - * @param {{ readable: ReadableStream<T>, writable: WritableStream<R> }} transform - * @param {PipeOptions=} options - * @returns {ReadableStream<T>} - */ - pipeThrough(transform, options = {}) { - webidl.assertBranded(this, ReadableStreamPrototype); - const prefix = "Failed to execute 'pipeThrough' on 'ReadableStream'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - transform = webidl.converters.ReadableWritablePair(transform, { - prefix, - context: "Argument 1", - }); - options = webidl.converters.StreamPipeOptions(options, { + /** + * @param {ArrayBufferView} chunk + * @returns {void} + */ + enqueue(chunk) { + webidl.assertBranded(this, ReadableByteStreamControllerPrototype); + const prefix = + "Failed to execute 'enqueue' on 'ReadableByteStreamController'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + const arg1 = "Argument 1"; + chunk = webidl.converters.ArrayBufferView(chunk, { + prefix, + context: arg1, + }); + if (chunk.byteLength === 0) { + throw webidl.makeException(TypeError, "length must be non-zero", { prefix, - context: "Argument 2", + context: arg1, }); - const { readable, writable } = transform; - const { preventClose, preventAbort, preventCancel, signal } = options; - if (isReadableStreamLocked(this)) { - throw new TypeError("ReadableStream is already locked."); - } - if (isWritableStreamLocked(writable)) { - throw new TypeError("Target WritableStream is already locked."); - } - const promise = readableStreamPipeTo( - this, - writable, - preventClose, - preventAbort, - preventCancel, - signal, - ); - setPromiseIsHandledToTrue(promise); - return readable; } - - /** - * @param {WritableStream<R>} destination - * @param {PipeOptions=} options - * @returns {Promise<void>} - */ - pipeTo(destination, options = {}) { - try { - webidl.assertBranded(this, ReadableStreamPrototype); - const prefix = "Failed to execute 'pipeTo' on 'ReadableStream'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - destination = webidl.converters.WritableStream(destination, { - prefix, - context: "Argument 1", - }); - options = webidl.converters.StreamPipeOptions(options, { - prefix, - context: "Argument 2", - }); - } catch (err) { - return PromiseReject(err); - } - const { preventClose, preventAbort, preventCancel, signal } = options; - if (isReadableStreamLocked(this)) { - return PromiseReject( - new TypeError("ReadableStream is already locked."), - ); - } - if (isWritableStreamLocked(destination)) { - return PromiseReject( - new TypeError("destination WritableStream is already locked."), - ); - } - return readableStreamPipeTo( - this, - destination, - preventClose, - preventAbort, - preventCancel, - signal, + if (chunk.buffer.byteLength === 0) { + throw webidl.makeException( + TypeError, + "buffer length must be non-zero", + { prefix, context: arg1 }, ); } - - /** @returns {[ReadableStream<R>, ReadableStream<R>]} */ - tee() { - webidl.assertBranded(this, ReadableStreamPrototype); - return readableStreamTee(this, false); + if (this[_closeRequested] === true) { + throw new TypeError( + "Cannot enqueue chunk after a close has been requested.", + ); } - - // TODO(lucacasonato): should be moved to webidl crate - /** - * @param {ReadableStreamIteratorOptions=} options - * @returns {AsyncIterableIterator<R>} - */ - values(options = {}) { - webidl.assertBranded(this, ReadableStreamPrototype); - const prefix = "Failed to execute 'values' on 'ReadableStream'"; - options = webidl.converters.ReadableStreamIteratorOptions(options, { - prefix, - context: "Argument 1", - }); - /** @type {AsyncIterableIterator<R>} */ - const iterator = ObjectCreate(readableStreamAsyncIteratorPrototype); - const reader = acquireReadableStreamDefaultReader(this); - iterator[_reader] = reader; - iterator[_preventCancel] = options.preventCancel; - return iterator; + if (this[_stream][_state] !== "readable") { + throw new TypeError( + "Cannot enqueue chunk when underlying stream is not readable.", + ); } + return readableByteStreamControllerEnqueue(this, chunk); + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return `${this.constructor.name} ${inspect({ locked: this.locked })}`; + /** + * @param {any=} e + * @returns {void} + */ + error(e = undefined) { + webidl.assertBranded(this, ReadableByteStreamControllerPrototype); + if (e !== undefined) { + e = webidl.converters.any(e); } + readableByteStreamControllerError(this, e); } - // TODO(lucacasonato): should be moved to webidl crate - ReadableStream.prototype[SymbolAsyncIterator] = - ReadableStream.prototype.values; - ObjectDefineProperty(ReadableStream.prototype, SymbolAsyncIterator, { - writable: true, - enumerable: false, - configurable: true, - }); - - webidl.configurePrototype(ReadableStream); - const ReadableStreamPrototype = ReadableStream.prototype; + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + ReadableByteStreamControllerPrototype, + this, + ), + keys: ["desiredSize"], + })); + } - function errorReadableStream(stream, e) { - readableStreamDefaultControllerError(stream[_controller], e); + /** + * @param {any} reason + * @returns {Promise<void>} + */ + [_cancelSteps](reason) { + readableByteStreamControllerClearPendingPullIntos(this); + resetQueue(this); + const result = this[_cancelAlgorithm](reason); + readableByteStreamControllerClearAlgorithms(this); + return result; } - /** @template R */ - class ReadableStreamDefaultReader { - /** @type {Deferred<void>} */ - [_closedPromise]; - /** @type {ReadableStream<R> | undefined} */ - [_stream]; - /** @type {ReadRequest[]} */ - [_readRequests]; - - /** @param {ReadableStream<R>} stream */ - constructor(stream) { - const prefix = "Failed to construct 'ReadableStreamDefaultReader'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - stream = webidl.converters.ReadableStream(stream, { - prefix, - context: "Argument 1", - }); - this[webidl.brand] = webidl.brand; - setUpReadableStreamDefaultReader(this, stream); + /** + * @param {ReadRequest<ArrayBuffer>} readRequest + * @returns {void} + */ + [_pullSteps](readRequest) { + /** @type {ReadableStream<ArrayBuffer>} */ + const stream = this[_stream]; + assert(readableStreamHasDefaultReader(stream)); + if (this[_queueTotalSize] > 0) { + assert(readableStreamGetNumReadRequests(stream) === 0); + readableByteStreamControllerFillReadRequestFromQueue(this, readRequest); + return; } - - /** @returns {Promise<ReadableStreamReadResult<R>>} */ - read() { + const autoAllocateChunkSize = this[_autoAllocateChunkSize]; + if (autoAllocateChunkSize !== undefined) { + let buffer; try { - webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); - } catch (err) { - return PromiseReject(err); - } - if (this[_stream] === undefined) { - return PromiseReject( - new TypeError("Reader has no associated stream."), - ); - } - /** @type {Deferred<ReadableStreamReadResult<R>>} */ - const promise = new Deferred(); - /** @type {ReadRequest<R>} */ - const readRequest = { - chunkSteps(chunk) { - promise.resolve({ value: chunk, done: false }); - }, - closeSteps() { - promise.resolve({ value: undefined, done: true }); - }, - errorSteps(e) { - promise.reject(e); - }, - }; - readableStreamDefaultReaderRead(this, readRequest); - return promise.promise; - } - - /** @returns {void} */ - releaseLock() { - webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); - if (this[_stream] === undefined) { + buffer = new ArrayBuffer(autoAllocateChunkSize); + } catch (e) { + readRequest.errorSteps(e); return; } - readableStreamDefaultReaderRelease(this); - } - - get closed() { - try { - webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); - } catch (err) { - return PromiseReject(err); - } - return this[_closedPromise].promise; - } - - /** - * @param {any} reason - * @returns {Promise<void>} - */ - cancel(reason = undefined) { - try { - webidl.assertBranded(this, ReadableStreamDefaultReaderPrototype); - if (reason !== undefined) { - reason = webidl.converters.any(reason); - } - } catch (err) { - return PromiseReject(err); - } - - if (this[_stream] === undefined) { - return PromiseReject( - new TypeError("Reader has no associated stream."), - ); - } - return readableStreamReaderGenericCancel(this, reason); + /** @type {PullIntoDescriptor} */ + const pullIntoDescriptor = { + buffer, + bufferByteLength: autoAllocateChunkSize, + byteOffset: 0, + byteLength: autoAllocateChunkSize, + bytesFilled: 0, + elementSize: 1, + viewConstructor: Uint8Array, + readerType: "default", + }; + ArrayPrototypePush(this[_pendingPullIntos], pullIntoDescriptor); } + readableStreamAddReadRequest(stream, readRequest); + readableByteStreamControllerCallPullIfNeeded(this); + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return `${this.constructor.name} ${inspect({ closed: this.closed })}`; + [_releaseSteps]() { + if (this[_pendingPullIntos].length !== 0) { + /** @type {PullIntoDescriptor} */ + const firstPendingPullInto = this[_pendingPullIntos][0]; + firstPendingPullInto.readerType = "none"; + this[_pendingPullIntos] = [firstPendingPullInto]; } } +} - webidl.configurePrototype(ReadableStreamDefaultReader); - const ReadableStreamDefaultReaderPrototype = - ReadableStreamDefaultReader.prototype; +webidl.configurePrototype(ReadableByteStreamController); +const ReadableByteStreamControllerPrototype = + ReadableByteStreamController.prototype; - /** @template R */ - class ReadableStreamBYOBReader { - /** @type {Deferred<void>} */ - [_closedPromise]; - /** @type {ReadableStream<R> | undefined} */ - [_stream]; - /** @type {ReadIntoRequest[]} */ - [_readIntoRequests]; - - /** @param {ReadableStream<R>} stream */ - constructor(stream) { - const prefix = "Failed to construct 'ReadableStreamBYOBReader'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - stream = webidl.converters.ReadableStream(stream, { - prefix, - context: "Argument 1", - }); - this[webidl.brand] = webidl.brand; - setUpReadableStreamBYOBReader(this, stream); - } +/** @template R */ +class ReadableStreamDefaultController { + /** @type {(reason: any) => Promise<void>} */ + [_cancelAlgorithm]; + /** @type {boolean} */ + [_closeRequested]; + /** @type {boolean} */ + [_pullAgain]; + /** @type {(controller: this) => Promise<void>} */ + [_pullAlgorithm]; + /** @type {boolean} */ + [_pulling]; + /** @type {Array<ValueWithSize<R>>} */ + [_queue]; + /** @type {number} */ + [_queueTotalSize]; + /** @type {boolean} */ + [_started]; + /** @type {number} */ + [_strategyHWM]; + /** @type {(chunk: R) => number} */ + [_strategySizeAlgorithm]; + /** @type {ReadableStream<R>} */ + [_stream]; - /** - * @param {ArrayBufferView} view - * @returns {Promise<ReadableStreamBYOBReadResult>} - */ - read(view) { - try { - webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); - const prefix = "Failed to execute 'read' on 'ReadableStreamBYOBReader'"; - view = webidl.converters.ArrayBufferView(view, { - prefix, - context: "Argument 1", - }); - } catch (err) { - return PromiseReject(err); - } + constructor() { + webidl.illegalConstructor(); + } - if (view.byteLength === 0) { - return PromiseReject( - new TypeError("view must have non-zero byteLength"), - ); - } - if (view.buffer.byteLength === 0) { - return PromiseReject( - new TypeError("view's buffer must have non-zero byteLength"), - ); - } - if (isDetachedBuffer(view.buffer)) { - return PromiseReject( - new TypeError("view's buffer has been detached"), - ); - } - if (this[_stream] === undefined) { - return PromiseReject( - new TypeError("Reader has no associated stream."), - ); - } - /** @type {Deferred<ReadableStreamBYOBReadResult>} */ - const promise = new Deferred(); - /** @type {ReadIntoRequest} */ - const readIntoRequest = { - chunkSteps(chunk) { - promise.resolve({ value: chunk, done: false }); - }, - closeSteps(chunk) { - promise.resolve({ value: chunk, done: true }); - }, - errorSteps(e) { - promise.reject(e); - }, - }; - readableStreamBYOBReaderRead(this, view, readIntoRequest); - return promise.promise; - } + /** @returns {number | null} */ + get desiredSize() { + webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); + return readableStreamDefaultControllerGetDesiredSize(this); + } - /** @returns {void} */ - releaseLock() { - webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); - if (this[_stream] === undefined) { - return; - } - readableStreamBYOBReaderRelease(this); + /** @returns {void} */ + close() { + webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); + if (readableStreamDefaultControllerCanCloseOrEnqueue(this) === false) { + throw new TypeError("The stream controller cannot close or enqueue."); } + readableStreamDefaultControllerClose(this); + } - get closed() { - try { - webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); - } catch (err) { - return PromiseReject(err); - } - return this[_closedPromise].promise; + /** + * @param {R} chunk + * @returns {void} + */ + enqueue(chunk = undefined) { + webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); + if (chunk !== undefined) { + chunk = webidl.converters.any(chunk); } - - /** - * @param {any} reason - * @returns {Promise<void>} - */ - cancel(reason = undefined) { - try { - webidl.assertBranded(this, ReadableStreamBYOBReaderPrototype); - if (reason !== undefined) { - reason = webidl.converters.any(reason); - } - } catch (err) { - return PromiseReject(err); - } - - if (this[_stream] === undefined) { - return PromiseReject( - new TypeError("Reader has no associated stream."), - ); - } - return readableStreamReaderGenericCancel(this, reason); + if (readableStreamDefaultControllerCanCloseOrEnqueue(this) === false) { + throw new TypeError("The stream controller cannot close or enqueue."); } + readableStreamDefaultControllerEnqueue(this, chunk); + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return `${this.constructor.name} ${inspect({ closed: this.closed })}`; + /** + * @param {any=} e + * @returns {void} + */ + error(e = undefined) { + webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); + if (e !== undefined) { + e = webidl.converters.any(e); } + readableStreamDefaultControllerError(this, e); } - webidl.configurePrototype(ReadableStreamBYOBReader); - const ReadableStreamBYOBReaderPrototype = ReadableStreamBYOBReader.prototype; + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + ReadableStreamDefaultController.prototype, + this, + ), + keys: ["desiredSize"], + })); + } - class ReadableStreamBYOBRequest { - /** @type {ReadableByteStreamController} */ - [_controller]; - /** @type {ArrayBufferView | null} */ - [_view]; + /** + * @param {any} reason + * @returns {Promise<void>} + */ + [_cancelSteps](reason) { + resetQueue(this); + const result = this[_cancelAlgorithm](reason); + readableStreamDefaultControllerClearAlgorithms(this); + return result; + } - /** @returns {ArrayBufferView | null} */ - get view() { - webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype); - return this[_view]; + /** + * @param {ReadRequest<R>} readRequest + * @returns {void} + */ + [_pullSteps](readRequest) { + const stream = this[_stream]; + if (this[_queue].length) { + const chunk = dequeueValue(this); + if (this[_closeRequested] && this[_queue].length === 0) { + readableStreamDefaultControllerClearAlgorithms(this); + readableStreamClose(stream); + } else { + readableStreamDefaultControllerCallPullIfNeeded(this); + } + readRequest.chunkSteps(chunk); + } else { + readableStreamAddReadRequest(stream, readRequest); + readableStreamDefaultControllerCallPullIfNeeded(this); } + } - constructor() { - webidl.illegalConstructor(); - } + [_releaseSteps]() { + return; + } +} - respond(bytesWritten) { - webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype); - const prefix = - "Failed to execute 'respond' on 'ReadableStreamBYOBRequest'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - bytesWritten = webidl.converters["unsigned long long"](bytesWritten, { - enforceRange: true, - prefix, - context: "Argument 1", - }); +webidl.configurePrototype(ReadableStreamDefaultController); +const ReadableStreamDefaultControllerPrototype = + ReadableStreamDefaultController.prototype; - if (this[_controller] === undefined) { - throw new TypeError("This BYOB request has been invalidated"); - } - if (isDetachedBuffer(this[_view].buffer)) { - throw new TypeError( - "The BYOB request's buffer has been detached and so cannot be used as a response", - ); - } - assert(this[_view].byteLength > 0); - assert(this[_view].buffer.byteLength > 0); - readableByteStreamControllerRespond(this[_controller], bytesWritten); - } +/** + * @template I + * @template O + */ +class TransformStream { + /** @type {boolean} */ + [_backpressure]; + /** @type {Deferred<void>} */ + [_backpressureChangePromise]; + /** @type {TransformStreamDefaultController<O>} */ + [_controller]; + /** @type {boolean} */ + [_detached]; + /** @type {ReadableStream<O>} */ + [_readable]; + /** @type {WritableStream<I>} */ + [_writable]; - respondWithNewView(view) { - webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype); - const prefix = - "Failed to execute 'respondWithNewView' on 'ReadableStreamBYOBRequest'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - view = webidl.converters.ArrayBufferView(view, { + /** + * @param {Transformer<I, O>} transformer + * @param {QueuingStrategy<I>} writableStrategy + * @param {QueuingStrategy<O>} readableStrategy + */ + constructor( + transformer = undefined, + writableStrategy = {}, + readableStrategy = {}, + ) { + const prefix = "Failed to construct 'TransformStream'"; + if (transformer !== undefined) { + transformer = webidl.converters.object(transformer, { prefix, context: "Argument 1", }); - - if (this[_controller] === undefined) { - throw new TypeError("This BYOB request has been invalidated"); - } - if (isDetachedBuffer(view.buffer)) { - throw new TypeError( - "The given view's buffer has been detached and so cannot be used as a response", - ); - } - readableByteStreamControllerRespondWithNewView(this[_controller], view); - } - } - - webidl.configurePrototype(ReadableStreamBYOBRequest); - const ReadableStreamBYOBRequestPrototype = - ReadableStreamBYOBRequest.prototype; - - class ReadableByteStreamController { - /** @type {number | undefined} */ - [_autoAllocateChunkSize]; - /** @type {ReadableStreamBYOBRequest | null} */ - [_byobRequest]; - /** @type {(reason: any) => Promise<void>} */ - [_cancelAlgorithm]; - /** @type {boolean} */ - [_closeRequested]; - /** @type {boolean} */ - [_pullAgain]; - /** @type {(controller: this) => Promise<void>} */ - [_pullAlgorithm]; - /** @type {boolean} */ - [_pulling]; - /** @type {PullIntoDescriptor[]} */ - [_pendingPullIntos]; - /** @type {ReadableByteStreamQueueEntry[]} */ - [_queue]; - /** @type {number} */ - [_queueTotalSize]; - /** @type {boolean} */ - [_started]; - /** @type {number} */ - [_strategyHWM]; - /** @type {ReadableStream<ArrayBuffer>} */ - [_stream]; - - constructor() { - webidl.illegalConstructor(); - } - - /** @returns {ReadableStreamBYOBRequest | null} */ - get byobRequest() { - webidl.assertBranded(this, ReadableByteStreamControllerPrototype); - return readableByteStreamControllerGetBYOBRequest(this); } - - /** @returns {number | null} */ - get desiredSize() { - webidl.assertBranded(this, ReadableByteStreamControllerPrototype); - return readableByteStreamControllerGetDesiredSize(this); - } - - /** @returns {void} */ - close() { - webidl.assertBranded(this, ReadableByteStreamControllerPrototype); - if (this[_closeRequested] === true) { - throw new TypeError("Closed already requested."); - } - if (this[_stream][_state] !== "readable") { - throw new TypeError( - "ReadableByteStreamController's stream is not in a readable state.", - ); - } - readableByteStreamControllerClose(this); + writableStrategy = webidl.converters.QueuingStrategy(writableStrategy, { + prefix, + context: "Argument 2", + }); + readableStrategy = webidl.converters.QueuingStrategy(readableStrategy, { + prefix, + context: "Argument 2", + }); + this[webidl.brand] = webidl.brand; + if (transformer === undefined) { + transformer = null; } - - /** - * @param {ArrayBufferView} chunk - * @returns {void} - */ - enqueue(chunk) { - webidl.assertBranded(this, ReadableByteStreamControllerPrototype); - const prefix = - "Failed to execute 'enqueue' on 'ReadableByteStreamController'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - const arg1 = "Argument 1"; - chunk = webidl.converters.ArrayBufferView(chunk, { - prefix, - context: arg1, - }); - if (chunk.byteLength === 0) { - throw webidl.makeException(TypeError, "length must be non-zero", { - prefix, - context: arg1, - }); - } - if (chunk.buffer.byteLength === 0) { - throw webidl.makeException( - TypeError, - "buffer length must be non-zero", - { prefix, context: arg1 }, - ); - } - if (this[_closeRequested] === true) { - throw new TypeError( - "Cannot enqueue chunk after a close has been requested.", - ); - } - if (this[_stream][_state] !== "readable") { - throw new TypeError( - "Cannot enqueue chunk when underlying stream is not readable.", - ); - } - return readableByteStreamControllerEnqueue(this, chunk); + const transformerDict = webidl.converters.Transformer(transformer, { + prefix, + context: "transformer", + }); + if (transformerDict.readableType !== undefined) { + throw new RangeError( + `${prefix}: readableType transformers not supported.`, + ); } - - /** - * @param {any=} e - * @returns {void} - */ - error(e = undefined) { - webidl.assertBranded(this, ReadableByteStreamControllerPrototype); - if (e !== undefined) { - e = webidl.converters.any(e); - } - readableByteStreamControllerError(this, e); + if (transformerDict.writableType !== undefined) { + throw new RangeError( + `${prefix}: writableType transformers not supported.`, + ); } - - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - ReadableByteStreamControllerPrototype, - this, + const readableHighWaterMark = extractHighWaterMark(readableStrategy, 0); + const readableSizeAlgorithm = extractSizeAlgorithm(readableStrategy); + const writableHighWaterMark = extractHighWaterMark(writableStrategy, 1); + const writableSizeAlgorithm = extractSizeAlgorithm(writableStrategy); + /** @type {Deferred<void>} */ + const startPromise = new Deferred(); + initializeTransformStream( + this, + startPromise, + writableHighWaterMark, + writableSizeAlgorithm, + readableHighWaterMark, + readableSizeAlgorithm, + ); + setUpTransformStreamDefaultControllerFromTransformer( + this, + transformer, + transformerDict, + ); + if (transformerDict.start) { + startPromise.resolve( + webidl.invokeCallbackFunction( + transformerDict.start, + [this[_controller]], + transformer, + webidl.converters.any, + { + prefix: + "Failed to call 'start' on 'TransformStreamDefaultController'", + }, ), - keys: ["desiredSize"], - })); - } - - /** - * @param {any} reason - * @returns {Promise<void>} - */ - [_cancelSteps](reason) { - readableByteStreamControllerClearPendingPullIntos(this); - resetQueue(this); - const result = this[_cancelAlgorithm](reason); - readableByteStreamControllerClearAlgorithms(this); - return result; - } - - /** - * @param {ReadRequest<ArrayBuffer>} readRequest - * @returns {void} - */ - [_pullSteps](readRequest) { - /** @type {ReadableStream<ArrayBuffer>} */ - const stream = this[_stream]; - assert(readableStreamHasDefaultReader(stream)); - if (this[_queueTotalSize] > 0) { - assert(readableStreamGetNumReadRequests(stream) === 0); - readableByteStreamControllerFillReadRequestFromQueue(this, readRequest); - return; - } - const autoAllocateChunkSize = this[_autoAllocateChunkSize]; - if (autoAllocateChunkSize !== undefined) { - let buffer; - try { - buffer = new ArrayBuffer(autoAllocateChunkSize); - } catch (e) { - readRequest.errorSteps(e); - return; - } - /** @type {PullIntoDescriptor} */ - const pullIntoDescriptor = { - buffer, - bufferByteLength: autoAllocateChunkSize, - byteOffset: 0, - byteLength: autoAllocateChunkSize, - bytesFilled: 0, - elementSize: 1, - viewConstructor: Uint8Array, - readerType: "default", - }; - ArrayPrototypePush(this[_pendingPullIntos], pullIntoDescriptor); - } - readableStreamAddReadRequest(stream, readRequest); - readableByteStreamControllerCallPullIfNeeded(this); + ); + } else { + startPromise.resolve(undefined); } + } - [_releaseSteps]() { - if (this[_pendingPullIntos].length !== 0) { - /** @type {PullIntoDescriptor} */ - const firstPendingPullInto = this[_pendingPullIntos][0]; - firstPendingPullInto.readerType = "none"; - this[_pendingPullIntos] = [firstPendingPullInto]; - } - } + /** @returns {ReadableStream<O>} */ + get readable() { + webidl.assertBranded(this, TransformStreamPrototype); + return this[_readable]; } - webidl.configurePrototype(ReadableByteStreamController); - const ReadableByteStreamControllerPrototype = - ReadableByteStreamController.prototype; - - /** @template R */ - class ReadableStreamDefaultController { - /** @type {(reason: any) => Promise<void>} */ - [_cancelAlgorithm]; - /** @type {boolean} */ - [_closeRequested]; - /** @type {boolean} */ - [_pullAgain]; - /** @type {(controller: this) => Promise<void>} */ - [_pullAlgorithm]; - /** @type {boolean} */ - [_pulling]; - /** @type {Array<ValueWithSize<R>>} */ - [_queue]; - /** @type {number} */ - [_queueTotalSize]; - /** @type {boolean} */ - [_started]; - /** @type {number} */ - [_strategyHWM]; - /** @type {(chunk: R) => number} */ - [_strategySizeAlgorithm]; - /** @type {ReadableStream<R>} */ - [_stream]; - - constructor() { - webidl.illegalConstructor(); - } - - /** @returns {number | null} */ - get desiredSize() { - webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); - return readableStreamDefaultControllerGetDesiredSize(this); - } - - /** @returns {void} */ - close() { - webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); - if (readableStreamDefaultControllerCanCloseOrEnqueue(this) === false) { - throw new TypeError("The stream controller cannot close or enqueue."); - } - readableStreamDefaultControllerClose(this); - } + /** @returns {WritableStream<I>} */ + get writable() { + webidl.assertBranded(this, TransformStreamPrototype); + return this[_writable]; + } - /** - * @param {R} chunk - * @returns {void} - */ - enqueue(chunk = undefined) { - webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); - if (chunk !== undefined) { - chunk = webidl.converters.any(chunk); - } - if (readableStreamDefaultControllerCanCloseOrEnqueue(this) === false) { - throw new TypeError("The stream controller cannot close or enqueue."); - } - readableStreamDefaultControllerEnqueue(this, chunk); - } + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return `${this.constructor.name} ${ + inspect({ readable: this.readable, writable: this.writable }) + }`; + } +} - /** - * @param {any=} e - * @returns {void} - */ - error(e = undefined) { - webidl.assertBranded(this, ReadableStreamDefaultControllerPrototype); - if (e !== undefined) { - e = webidl.converters.any(e); - } - readableStreamDefaultControllerError(this, e); - } +webidl.configurePrototype(TransformStream); +const TransformStreamPrototype = TransformStream.prototype; - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - ReadableStreamDefaultController.prototype, - this, - ), - keys: ["desiredSize"], - })); - } - - /** - * @param {any} reason - * @returns {Promise<void>} - */ - [_cancelSteps](reason) { - resetQueue(this); - const result = this[_cancelAlgorithm](reason); - readableStreamDefaultControllerClearAlgorithms(this); - return result; - } - - /** - * @param {ReadRequest<R>} readRequest - * @returns {void} - */ - [_pullSteps](readRequest) { - const stream = this[_stream]; - if (this[_queue].length) { - const chunk = dequeueValue(this); - if (this[_closeRequested] && this[_queue].length === 0) { - readableStreamDefaultControllerClearAlgorithms(this); - readableStreamClose(stream); - } else { - readableStreamDefaultControllerCallPullIfNeeded(this); - } - readRequest.chunkSteps(chunk); - } else { - readableStreamAddReadRequest(stream, readRequest); - readableStreamDefaultControllerCallPullIfNeeded(this); - } - } +/** @template O */ +class TransformStreamDefaultController { + /** @type {(controller: this) => Promise<void>} */ + [_flushAlgorithm]; + /** @type {TransformStream<O>} */ + [_stream]; + /** @type {(chunk: O, controller: this) => Promise<void>} */ + [_transformAlgorithm]; - [_releaseSteps]() { - return; - } + constructor() { + webidl.illegalConstructor(); } - webidl.configurePrototype(ReadableStreamDefaultController); - const ReadableStreamDefaultControllerPrototype = - ReadableStreamDefaultController.prototype; + /** @returns {number | null} */ + get desiredSize() { + webidl.assertBranded(this, TransformStreamDefaultController.prototype); + const readableController = this[_stream][_readable][_controller]; + return readableStreamDefaultControllerGetDesiredSize( + /** @type {ReadableStreamDefaultController<O>} */ readableController, + ); + } /** - * @template I - * @template O + * @param {O} chunk + * @returns {void} */ - class TransformStream { - /** @type {boolean} */ - [_backpressure]; - /** @type {Deferred<void>} */ - [_backpressureChangePromise]; - /** @type {TransformStreamDefaultController<O>} */ - [_controller]; - /** @type {boolean} */ - [_detached]; - /** @type {ReadableStream<O>} */ - [_readable]; - /** @type {WritableStream<I>} */ - [_writable]; - - /** - * @param {Transformer<I, O>} transformer - * @param {QueuingStrategy<I>} writableStrategy - * @param {QueuingStrategy<O>} readableStrategy - */ - constructor( - transformer = undefined, - writableStrategy = {}, - readableStrategy = {}, - ) { - const prefix = "Failed to construct 'TransformStream'"; - if (transformer !== undefined) { - transformer = webidl.converters.object(transformer, { - prefix, - context: "Argument 1", - }); - } - writableStrategy = webidl.converters.QueuingStrategy(writableStrategy, { - prefix, - context: "Argument 2", - }); - readableStrategy = webidl.converters.QueuingStrategy(readableStrategy, { - prefix, - context: "Argument 2", - }); - this[webidl.brand] = webidl.brand; - if (transformer === undefined) { - transformer = null; - } - const transformerDict = webidl.converters.Transformer(transformer, { - prefix, - context: "transformer", - }); - if (transformerDict.readableType !== undefined) { - throw new RangeError( - `${prefix}: readableType transformers not supported.`, - ); - } - if (transformerDict.writableType !== undefined) { - throw new RangeError( - `${prefix}: writableType transformers not supported.`, - ); - } - const readableHighWaterMark = extractHighWaterMark(readableStrategy, 0); - const readableSizeAlgorithm = extractSizeAlgorithm(readableStrategy); - const writableHighWaterMark = extractHighWaterMark(writableStrategy, 1); - const writableSizeAlgorithm = extractSizeAlgorithm(writableStrategy); - /** @type {Deferred<void>} */ - const startPromise = new Deferred(); - initializeTransformStream( - this, - startPromise, - writableHighWaterMark, - writableSizeAlgorithm, - readableHighWaterMark, - readableSizeAlgorithm, - ); - setUpTransformStreamDefaultControllerFromTransformer( - this, - transformer, - transformerDict, - ); - if (transformerDict.start) { - startPromise.resolve( - webidl.invokeCallbackFunction( - transformerDict.start, - [this[_controller]], - transformer, - webidl.converters.any, - { - prefix: - "Failed to call 'start' on 'TransformStreamDefaultController'", - }, - ), - ); - } else { - startPromise.resolve(undefined); - } - } - - /** @returns {ReadableStream<O>} */ - get readable() { - webidl.assertBranded(this, TransformStreamPrototype); - return this[_readable]; - } - - /** @returns {WritableStream<I>} */ - get writable() { - webidl.assertBranded(this, TransformStreamPrototype); - return this[_writable]; + enqueue(chunk = undefined) { + webidl.assertBranded(this, TransformStreamDefaultController.prototype); + if (chunk !== undefined) { + chunk = webidl.converters.any(chunk); } + transformStreamDefaultControllerEnqueue(this, chunk); + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return `${this.constructor.name} ${ - inspect({ readable: this.readable, writable: this.writable }) - }`; + /** + * @param {any=} reason + * @returns {void} + */ + error(reason = undefined) { + webidl.assertBranded(this, TransformStreamDefaultController.prototype); + if (reason !== undefined) { + reason = webidl.converters.any(reason); } + transformStreamDefaultControllerError(this, reason); } - webidl.configurePrototype(TransformStream); - const TransformStreamPrototype = TransformStream.prototype; - - /** @template O */ - class TransformStreamDefaultController { - /** @type {(controller: this) => Promise<void>} */ - [_flushAlgorithm]; - /** @type {TransformStream<O>} */ - [_stream]; - /** @type {(chunk: O, controller: this) => Promise<void>} */ - [_transformAlgorithm]; + /** @returns {void} */ + terminate() { + webidl.assertBranded(this, TransformStreamDefaultControllerPrototype); + transformStreamDefaultControllerTerminate(this); + } - constructor() { - webidl.illegalConstructor(); + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + TransformStreamDefaultController.prototype, + this, + ), + keys: ["desiredSize"], + })); + } +} + +webidl.configurePrototype(TransformStreamDefaultController); +const TransformStreamDefaultControllerPrototype = + TransformStreamDefaultController.prototype; + +/** @template W */ +class WritableStream { + /** @type {boolean} */ + [_backpressure]; + /** @type {Deferred<void> | undefined} */ + [_closeRequest]; + /** @type {WritableStreamDefaultController<W>} */ + [_controller]; + /** @type {boolean} */ + [_detached]; + /** @type {Deferred<void> | undefined} */ + [_inFlightWriteRequest]; + /** @type {Deferred<void> | undefined} */ + [_inFlightCloseRequest]; + /** @type {PendingAbortRequest | undefined} */ + [_pendingAbortRequest]; + /** @type {"writable" | "closed" | "erroring" | "errored"} */ + [_state]; + /** @type {any} */ + [_storedError]; + /** @type {WritableStreamDefaultWriter<W>} */ + [_writer]; + /** @type {Deferred<void>[]} */ + [_writeRequests]; + + /** + * @param {UnderlyingSink<W>=} underlyingSink + * @param {QueuingStrategy<W>=} strategy + */ + constructor(underlyingSink = undefined, strategy = {}) { + const prefix = "Failed to construct 'WritableStream'"; + if (underlyingSink !== undefined) { + underlyingSink = webidl.converters.object(underlyingSink, { + prefix, + context: "Argument 1", + }); } - - /** @returns {number | null} */ - get desiredSize() { - webidl.assertBranded(this, TransformStreamDefaultController.prototype); - const readableController = this[_stream][_readable][_controller]; - return readableStreamDefaultControllerGetDesiredSize( - /** @type {ReadableStreamDefaultController<O>} */ readableController, + strategy = webidl.converters.QueuingStrategy(strategy, { + prefix, + context: "Argument 2", + }); + this[webidl.brand] = webidl.brand; + if (underlyingSink === undefined) { + underlyingSink = null; + } + const underlyingSinkDict = webidl.converters.UnderlyingSink( + underlyingSink, + { prefix, context: "underlyingSink" }, + ); + if (underlyingSinkDict.type != null) { + throw new RangeError( + `${prefix}: WritableStream does not support 'type' in the underlying sink.`, ); } + initializeWritableStream(this); + const sizeAlgorithm = extractSizeAlgorithm(strategy); + const highWaterMark = extractHighWaterMark(strategy, 1); + setUpWritableStreamDefaultControllerFromUnderlyingSink( + this, + underlyingSink, + underlyingSinkDict, + highWaterMark, + sizeAlgorithm, + ); + } - /** - * @param {O} chunk - * @returns {void} - */ - enqueue(chunk = undefined) { - webidl.assertBranded(this, TransformStreamDefaultController.prototype); - if (chunk !== undefined) { - chunk = webidl.converters.any(chunk); - } - transformStreamDefaultControllerEnqueue(this, chunk); - } + /** @returns {boolean} */ + get locked() { + webidl.assertBranded(this, WritableStreamPrototype); + return isWritableStreamLocked(this); + } - /** - * @param {any=} reason - * @returns {void} - */ - error(reason = undefined) { - webidl.assertBranded(this, TransformStreamDefaultController.prototype); - if (reason !== undefined) { - reason = webidl.converters.any(reason); - } - transformStreamDefaultControllerError(this, reason); + /** + * @param {any=} reason + * @returns {Promise<void>} + */ + abort(reason = undefined) { + try { + webidl.assertBranded(this, WritableStreamPrototype); + } catch (err) { + return PromiseReject(err); } - - /** @returns {void} */ - terminate() { - webidl.assertBranded(this, TransformStreamDefaultControllerPrototype); - transformStreamDefaultControllerTerminate(this); + if (reason !== undefined) { + reason = webidl.converters.any(reason); } - - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - TransformStreamDefaultController.prototype, - this, + if (isWritableStreamLocked(this)) { + return PromiseReject( + new TypeError( + "The writable stream is locked, therefore cannot be aborted.", ), - keys: ["desiredSize"], - })); - } - } - - webidl.configurePrototype(TransformStreamDefaultController); - const TransformStreamDefaultControllerPrototype = - TransformStreamDefaultController.prototype; - - /** @template W */ - class WritableStream { - /** @type {boolean} */ - [_backpressure]; - /** @type {Deferred<void> | undefined} */ - [_closeRequest]; - /** @type {WritableStreamDefaultController<W>} */ - [_controller]; - /** @type {boolean} */ - [_detached]; - /** @type {Deferred<void> | undefined} */ - [_inFlightWriteRequest]; - /** @type {Deferred<void> | undefined} */ - [_inFlightCloseRequest]; - /** @type {PendingAbortRequest | undefined} */ - [_pendingAbortRequest]; - /** @type {"writable" | "closed" | "erroring" | "errored"} */ - [_state]; - /** @type {any} */ - [_storedError]; - /** @type {WritableStreamDefaultWriter<W>} */ - [_writer]; - /** @type {Deferred<void>[]} */ - [_writeRequests]; - - /** - * @param {UnderlyingSink<W>=} underlyingSink - * @param {QueuingStrategy<W>=} strategy - */ - constructor(underlyingSink = undefined, strategy = {}) { - const prefix = "Failed to construct 'WritableStream'"; - if (underlyingSink !== undefined) { - underlyingSink = webidl.converters.object(underlyingSink, { - prefix, - context: "Argument 1", - }); - } - strategy = webidl.converters.QueuingStrategy(strategy, { - prefix, - context: "Argument 2", - }); - this[webidl.brand] = webidl.brand; - if (underlyingSink === undefined) { - underlyingSink = null; - } - const underlyingSinkDict = webidl.converters.UnderlyingSink( - underlyingSink, - { prefix, context: "underlyingSink" }, - ); - if (underlyingSinkDict.type != null) { - throw new RangeError( - `${prefix}: WritableStream does not support 'type' in the underlying sink.`, - ); - } - initializeWritableStream(this); - const sizeAlgorithm = extractSizeAlgorithm(strategy); - const highWaterMark = extractHighWaterMark(strategy, 1); - setUpWritableStreamDefaultControllerFromUnderlyingSink( - this, - underlyingSink, - underlyingSinkDict, - highWaterMark, - sizeAlgorithm, ); } + return writableStreamAbort(this, reason); + } - /** @returns {boolean} */ - get locked() { + /** @returns {Promise<void>} */ + close() { + try { webidl.assertBranded(this, WritableStreamPrototype); - return isWritableStreamLocked(this); + } catch (err) { + return PromiseReject(err); } - - /** - * @param {any=} reason - * @returns {Promise<void>} - */ - abort(reason = undefined) { - try { - webidl.assertBranded(this, WritableStreamPrototype); - } catch (err) { - return PromiseReject(err); - } - if (reason !== undefined) { - reason = webidl.converters.any(reason); - } - if (isWritableStreamLocked(this)) { - return PromiseReject( - new TypeError( - "The writable stream is locked, therefore cannot be aborted.", - ), - ); - } - return writableStreamAbort(this, reason); + if (isWritableStreamLocked(this)) { + return PromiseReject( + new TypeError( + "The writable stream is locked, therefore cannot be closed.", + ), + ); } - - /** @returns {Promise<void>} */ - close() { - try { - webidl.assertBranded(this, WritableStreamPrototype); - } catch (err) { - return PromiseReject(err); - } - if (isWritableStreamLocked(this)) { - return PromiseReject( - new TypeError( - "The writable stream is locked, therefore cannot be closed.", - ), - ); - } - if (writableStreamCloseQueuedOrInFlight(this) === true) { - return PromiseReject( - new TypeError("The writable stream is already closing."), - ); - } - return writableStreamClose(this); + if (writableStreamCloseQueuedOrInFlight(this) === true) { + return PromiseReject( + new TypeError("The writable stream is already closing."), + ); } + return writableStreamClose(this); + } - /** @returns {WritableStreamDefaultWriter<W>} */ - getWriter() { - webidl.assertBranded(this, WritableStreamPrototype); - return acquireWritableStreamDefaultWriter(this); - } + /** @returns {WritableStreamDefaultWriter<W>} */ + getWriter() { + webidl.assertBranded(this, WritableStreamPrototype); + return acquireWritableStreamDefaultWriter(this); + } - [SymbolFor("Deno.privateCustomInspect")](inspect) { - return `${this.constructor.name} ${inspect({ locked: this.locked })}`; - } + [SymbolFor("Deno.privateCustomInspect")](inspect) { + return `${this.constructor.name} ${inspect({ locked: this.locked })}`; } +} - webidl.configurePrototype(WritableStream); - const WritableStreamPrototype = WritableStream.prototype; +webidl.configurePrototype(WritableStream); +const WritableStreamPrototype = WritableStream.prototype; - /** @template W */ - class WritableStreamDefaultWriter { - /** @type {Deferred<void>} */ - [_closedPromise]; +/** @template W */ +class WritableStreamDefaultWriter { + /** @type {Deferred<void>} */ + [_closedPromise]; - /** @type {Deferred<void>} */ - [_readyPromise]; + /** @type {Deferred<void>} */ + [_readyPromise]; - /** @type {WritableStream<W>} */ - [_stream]; + /** @type {WritableStream<W>} */ + [_stream]; - /** - * @param {WritableStream<W>} stream - */ - constructor(stream) { - const prefix = "Failed to construct 'WritableStreamDefaultWriter'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - stream = webidl.converters.WritableStream(stream, { - prefix, - context: "Argument 1", - }); - this[webidl.brand] = webidl.brand; - setUpWritableStreamDefaultWriter(this, stream); + /** + * @param {WritableStream<W>} stream + */ + constructor(stream) { + const prefix = "Failed to construct 'WritableStreamDefaultWriter'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + stream = webidl.converters.WritableStream(stream, { + prefix, + context: "Argument 1", + }); + this[webidl.brand] = webidl.brand; + setUpWritableStreamDefaultWriter(this, stream); + } + + /** @returns {Promise<void>} */ + get closed() { + try { + webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); + } catch (err) { + return PromiseReject(err); } + return this[_closedPromise].promise; + } - /** @returns {Promise<void>} */ - get closed() { - try { - webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); - } catch (err) { - return PromiseReject(err); - } - return this[_closedPromise].promise; + /** @returns {number} */ + get desiredSize() { + webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); + if (this[_stream] === undefined) { + throw new TypeError( + "A writable stream is not associated with the writer.", + ); } + return writableStreamDefaultWriterGetDesiredSize(this); + } - /** @returns {number} */ - get desiredSize() { + /** @returns {Promise<void>} */ + get ready() { + try { webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); - if (this[_stream] === undefined) { - throw new TypeError( - "A writable stream is not associated with the writer.", - ); - } - return writableStreamDefaultWriterGetDesiredSize(this); + } catch (err) { + return PromiseReject(err); } + return this[_readyPromise].promise; + } - /** @returns {Promise<void>} */ - get ready() { - try { - webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); - } catch (err) { - return PromiseReject(err); - } - return this[_readyPromise].promise; + /** + * @param {any} reason + * @returns {Promise<void>} + */ + abort(reason = undefined) { + try { + webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); + } catch (err) { + return PromiseReject(err); } - - /** - * @param {any} reason - * @returns {Promise<void>} - */ - abort(reason = undefined) { - try { - webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); - } catch (err) { - return PromiseReject(err); - } - if (reason !== undefined) { - reason = webidl.converters.any(reason); - } - if (this[_stream] === undefined) { - return PromiseReject( - new TypeError("A writable stream is not associated with the writer."), - ); - } - return writableStreamDefaultWriterAbort(this, reason); + if (reason !== undefined) { + reason = webidl.converters.any(reason); } - - /** @returns {Promise<void>} */ - close() { - try { - webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); - } catch (err) { - return PromiseReject(err); - } - const stream = this[_stream]; - if (stream === undefined) { - return PromiseReject( - new TypeError("A writable stream is not associated with the writer."), - ); - } - if (writableStreamCloseQueuedOrInFlight(stream) === true) { - return PromiseReject( - new TypeError("The associated stream is already closing."), - ); - } - return writableStreamDefaultWriterClose(this); + if (this[_stream] === undefined) { + return PromiseReject( + new TypeError("A writable stream is not associated with the writer."), + ); } + return writableStreamDefaultWriterAbort(this, reason); + } - /** @returns {void} */ - releaseLock() { + /** @returns {Promise<void>} */ + close() { + try { webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); - const stream = this[_stream]; - if (stream === undefined) { - return; - } - assert(stream[_writer] !== undefined); - writableStreamDefaultWriterRelease(this); + } catch (err) { + return PromiseReject(err); } + const stream = this[_stream]; + if (stream === undefined) { + return PromiseReject( + new TypeError("A writable stream is not associated with the writer."), + ); + } + if (writableStreamCloseQueuedOrInFlight(stream) === true) { + return PromiseReject( + new TypeError("The associated stream is already closing."), + ); + } + return writableStreamDefaultWriterClose(this); + } - /** - * @param {W} chunk - * @returns {Promise<void>} - */ - write(chunk = undefined) { - try { - webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); - if (chunk !== undefined) { - chunk = webidl.converters.any(chunk); - } - } catch (err) { - return PromiseReject(err); - } - if (this[_stream] === undefined) { - return PromiseReject( - new TypeError("A writable stream is not associate with the writer."), - ); - } - return writableStreamDefaultWriterWrite(this, chunk); + /** @returns {void} */ + releaseLock() { + webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); + const stream = this[_stream]; + if (stream === undefined) { + return; } + assert(stream[_writer] !== undefined); + writableStreamDefaultWriterRelease(this); + } - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - WritableStreamDefaultWriter.prototype, - this, - ), - keys: [ - "closed", - "desiredSize", - "ready", - ], - })); - } - } - - webidl.configurePrototype(WritableStreamDefaultWriter); - const WritableStreamDefaultWriterPrototype = - WritableStreamDefaultWriter.prototype; - - /** @template W */ - class WritableStreamDefaultController { - /** @type {(reason?: any) => Promise<void>} */ - [_abortAlgorithm]; - /** @type {() => Promise<void>} */ - [_closeAlgorithm]; - /** @type {ValueWithSize<W | _close>[]} */ - [_queue]; - /** @type {number} */ - [_queueTotalSize]; - /** @type {boolean} */ - [_started]; - /** @type {number} */ - [_strategyHWM]; - /** @type {(chunk: W) => number} */ - [_strategySizeAlgorithm]; - /** @type {WritableStream<W>} */ - [_stream]; - /** @type {(chunk: W, controller: this) => Promise<void>} */ - [_writeAlgorithm]; - /** @type {AbortSignal} */ - [_signal]; - - get signal() { - webidl.assertBranded(this, WritableStreamDefaultControllerPrototype); - return this[_signal]; - } - - constructor() { - webidl.illegalConstructor(); - } - - /** - * @param {any=} e - * @returns {void} - */ - error(e = undefined) { - webidl.assertBranded(this, WritableStreamDefaultControllerPrototype); - if (e !== undefined) { - e = webidl.converters.any(e); - } - const state = this[_stream][_state]; - if (state !== "writable") { - return; + /** + * @param {W} chunk + * @returns {Promise<void>} + */ + write(chunk = undefined) { + try { + webidl.assertBranded(this, WritableStreamDefaultWriterPrototype); + if (chunk !== undefined) { + chunk = webidl.converters.any(chunk); } - writableStreamDefaultControllerError(this, e); + } catch (err) { + return PromiseReject(err); } - - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - WritableStreamDefaultController.prototype, - this, - ), - keys: [], - })); + if (this[_stream] === undefined) { + return PromiseReject( + new TypeError("A writable stream is not associate with the writer."), + ); } + return writableStreamDefaultWriterWrite(this, chunk); + } - /** - * @param {any=} reason - * @returns {Promise<void>} - */ - [_abortSteps](reason) { - const result = this[_abortAlgorithm](reason); - writableStreamDefaultControllerClearAlgorithms(this); - return result; - } + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + WritableStreamDefaultWriter.prototype, + this, + ), + keys: [ + "closed", + "desiredSize", + "ready", + ], + })); + } +} + +webidl.configurePrototype(WritableStreamDefaultWriter); +const WritableStreamDefaultWriterPrototype = + WritableStreamDefaultWriter.prototype; + +/** @template W */ +class WritableStreamDefaultController { + /** @type {(reason?: any) => Promise<void>} */ + [_abortAlgorithm]; + /** @type {() => Promise<void>} */ + [_closeAlgorithm]; + /** @type {ValueWithSize<W | _close>[]} */ + [_queue]; + /** @type {number} */ + [_queueTotalSize]; + /** @type {boolean} */ + [_started]; + /** @type {number} */ + [_strategyHWM]; + /** @type {(chunk: W) => number} */ + [_strategySizeAlgorithm]; + /** @type {WritableStream<W>} */ + [_stream]; + /** @type {(chunk: W, controller: this) => Promise<void>} */ + [_writeAlgorithm]; + /** @type {AbortSignal} */ + [_signal]; + + get signal() { + webidl.assertBranded(this, WritableStreamDefaultControllerPrototype); + return this[_signal]; + } + + constructor() { + webidl.illegalConstructor(); + } - [_errorSteps]() { - resetQueue(this); + /** + * @param {any=} e + * @returns {void} + */ + error(e = undefined) { + webidl.assertBranded(this, WritableStreamDefaultControllerPrototype); + if (e !== undefined) { + e = webidl.converters.any(e); + } + const state = this[_stream][_state]; + if (state !== "writable") { + return; } + writableStreamDefaultControllerError(this, e); } - webidl.configurePrototype(WritableStreamDefaultController); - const WritableStreamDefaultControllerPrototype = - WritableStreamDefaultController.prototype; + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + WritableStreamDefaultController.prototype, + this, + ), + keys: [], + })); + } /** - * @param {ReadableStream} stream + * @param {any=} reason + * @returns {Promise<void>} */ - function createProxy(stream) { - return stream.pipeThrough(new TransformStream()); + [_abortSteps](reason) { + const result = this[_abortAlgorithm](reason); + writableStreamDefaultControllerClearAlgorithms(this); + return result; } - webidl.converters.ReadableStream = webidl - .createInterfaceConverter("ReadableStream", ReadableStream.prototype); - webidl.converters.WritableStream = webidl - .createInterfaceConverter("WritableStream", WritableStream.prototype); + [_errorSteps]() { + resetQueue(this); + } +} - webidl.converters.ReadableStreamType = webidl.createEnumConverter( - "ReadableStreamType", - ["bytes"], - ); +webidl.configurePrototype(WritableStreamDefaultController); +const WritableStreamDefaultControllerPrototype = + WritableStreamDefaultController.prototype; - webidl.converters.UnderlyingSource = webidl - .createDictionaryConverter("UnderlyingSource", [ - { - key: "start", - converter: webidl.converters.Function, - }, - { - key: "pull", - converter: webidl.converters.Function, - }, - { - key: "cancel", - converter: webidl.converters.Function, - }, - { - key: "type", - converter: webidl.converters.ReadableStreamType, - }, - { - key: "autoAllocateChunkSize", - converter: (V, opts) => - webidl.converters["unsigned long long"](V, { - ...opts, - enforceRange: true, - }), - }, - ]); - webidl.converters.UnderlyingSink = webidl - .createDictionaryConverter("UnderlyingSink", [ - { - key: "start", - converter: webidl.converters.Function, - }, - { - key: "write", - converter: webidl.converters.Function, - }, - { - key: "close", - converter: webidl.converters.Function, - }, - { - key: "abort", - converter: webidl.converters.Function, - }, - { - key: "type", - converter: webidl.converters.any, - }, - ]); - webidl.converters.Transformer = webidl - .createDictionaryConverter("Transformer", [ - { - key: "start", - converter: webidl.converters.Function, - }, - { - key: "transform", - converter: webidl.converters.Function, - }, - { - key: "flush", - converter: webidl.converters.Function, - }, - { - key: "readableType", - converter: webidl.converters.any, - }, - { - key: "writableType", - converter: webidl.converters.any, - }, - ]); - webidl.converters.QueuingStrategy = webidl - .createDictionaryConverter("QueuingStrategy", [ - { - key: "highWaterMark", - converter: webidl.converters["unrestricted double"], - }, - { - key: "size", - converter: webidl.converters.Function, - }, - ]); - webidl.converters.QueuingStrategyInit = webidl - .createDictionaryConverter("QueuingStrategyInit", [ - { - key: "highWaterMark", - converter: webidl.converters["unrestricted double"], - required: true, - }, - ]); - - webidl.converters.ReadableStreamIteratorOptions = webidl - .createDictionaryConverter("ReadableStreamIteratorOptions", [ - { - key: "preventCancel", - defaultValue: false, - converter: webidl.converters.boolean, - }, - ]); - - webidl.converters.ReadableStreamReaderMode = webidl - .createEnumConverter("ReadableStreamReaderMode", ["byob"]); - webidl.converters.ReadableStreamGetReaderOptions = webidl - .createDictionaryConverter("ReadableStreamGetReaderOptions", [{ - key: "mode", - converter: webidl.converters.ReadableStreamReaderMode, - }]); - - webidl.converters.ReadableWritablePair = webidl - .createDictionaryConverter("ReadableWritablePair", [ - { - key: "readable", - converter: webidl.converters.ReadableStream, - required: true, - }, - { - key: "writable", - converter: webidl.converters.WritableStream, - required: true, - }, - ]); - webidl.converters.StreamPipeOptions = webidl - .createDictionaryConverter("StreamPipeOptions", [ - { - key: "preventClose", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "preventAbort", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { - key: "preventCancel", - defaultValue: false, - converter: webidl.converters.boolean, - }, - { key: "signal", converter: webidl.converters.AbortSignal }, - ]); - - window.__bootstrap.streams = { - // Non-Public - _state, - isReadableStreamDisturbed, - errorReadableStream, - createProxy, - writableStreamClose, - readableStreamClose, - readableStreamCollectIntoUint8Array, - readableStreamDisturb, - readableStreamForRid, - readableStreamForRidUnrefable, - readableStreamForRidUnrefableRef, - readableStreamForRidUnrefableUnref, - readableStreamThrowIfErrored, - getReadableStreamResourceBacking, - writableStreamForRid, - getWritableStreamResourceBacking, - Deferred, - // Exposed in global runtime scope - ByteLengthQueuingStrategy, - CountQueuingStrategy, - ReadableStream, - ReadableStreamPrototype, - ReadableStreamDefaultReader, - TransformStream, - WritableStream, - WritableStreamDefaultWriter, - WritableStreamDefaultController, - ReadableByteStreamController, - ReadableStreamBYOBReader, - ReadableStreamBYOBRequest, - ReadableStreamDefaultController, - TransformStreamDefaultController, - }; -})(this); +/** + * @param {ReadableStream} stream + */ +function createProxy(stream) { + return stream.pipeThrough(new TransformStream()); +} + +webidl.converters.ReadableStream = webidl + .createInterfaceConverter("ReadableStream", ReadableStream.prototype); +webidl.converters.WritableStream = webidl + .createInterfaceConverter("WritableStream", WritableStream.prototype); + +webidl.converters.ReadableStreamType = webidl.createEnumConverter( + "ReadableStreamType", + ["bytes"], +); + +webidl.converters.UnderlyingSource = webidl + .createDictionaryConverter("UnderlyingSource", [ + { + key: "start", + converter: webidl.converters.Function, + }, + { + key: "pull", + converter: webidl.converters.Function, + }, + { + key: "cancel", + converter: webidl.converters.Function, + }, + { + key: "type", + converter: webidl.converters.ReadableStreamType, + }, + { + key: "autoAllocateChunkSize", + converter: (V, opts) => + webidl.converters["unsigned long long"](V, { + ...opts, + enforceRange: true, + }), + }, + ]); +webidl.converters.UnderlyingSink = webidl + .createDictionaryConverter("UnderlyingSink", [ + { + key: "start", + converter: webidl.converters.Function, + }, + { + key: "write", + converter: webidl.converters.Function, + }, + { + key: "close", + converter: webidl.converters.Function, + }, + { + key: "abort", + converter: webidl.converters.Function, + }, + { + key: "type", + converter: webidl.converters.any, + }, + ]); +webidl.converters.Transformer = webidl + .createDictionaryConverter("Transformer", [ + { + key: "start", + converter: webidl.converters.Function, + }, + { + key: "transform", + converter: webidl.converters.Function, + }, + { + key: "flush", + converter: webidl.converters.Function, + }, + { + key: "readableType", + converter: webidl.converters.any, + }, + { + key: "writableType", + converter: webidl.converters.any, + }, + ]); +webidl.converters.QueuingStrategy = webidl + .createDictionaryConverter("QueuingStrategy", [ + { + key: "highWaterMark", + converter: webidl.converters["unrestricted double"], + }, + { + key: "size", + converter: webidl.converters.Function, + }, + ]); +webidl.converters.QueuingStrategyInit = webidl + .createDictionaryConverter("QueuingStrategyInit", [ + { + key: "highWaterMark", + converter: webidl.converters["unrestricted double"], + required: true, + }, + ]); + +webidl.converters.ReadableStreamIteratorOptions = webidl + .createDictionaryConverter("ReadableStreamIteratorOptions", [ + { + key: "preventCancel", + defaultValue: false, + converter: webidl.converters.boolean, + }, + ]); + +webidl.converters.ReadableStreamReaderMode = webidl + .createEnumConverter("ReadableStreamReaderMode", ["byob"]); +webidl.converters.ReadableStreamGetReaderOptions = webidl + .createDictionaryConverter("ReadableStreamGetReaderOptions", [{ + key: "mode", + converter: webidl.converters.ReadableStreamReaderMode, + }]); + +webidl.converters.ReadableWritablePair = webidl + .createDictionaryConverter("ReadableWritablePair", [ + { + key: "readable", + converter: webidl.converters.ReadableStream, + required: true, + }, + { + key: "writable", + converter: webidl.converters.WritableStream, + required: true, + }, + ]); +webidl.converters.StreamPipeOptions = webidl + .createDictionaryConverter("StreamPipeOptions", [ + { + key: "preventClose", + defaultValue: false, + converter: webidl.converters.boolean, + }, + { + key: "preventAbort", + defaultValue: false, + converter: webidl.converters.boolean, + }, + { + key: "preventCancel", + defaultValue: false, + converter: webidl.converters.boolean, + }, + { key: "signal", converter: webidl.converters.AbortSignal }, + ]); + +export { + // Non-Public + _state, + // Exposed in global runtime scope + ByteLengthQueuingStrategy, + CountQueuingStrategy, + createProxy, + Deferred, + errorReadableStream, + getReadableStreamResourceBacking, + getWritableStreamResourceBacking, + isReadableStreamDisturbed, + ReadableByteStreamController, + ReadableStream, + ReadableStreamBYOBReader, + ReadableStreamBYOBRequest, + readableStreamClose, + readableStreamCollectIntoUint8Array, + ReadableStreamDefaultController, + ReadableStreamDefaultReader, + readableStreamDisturb, + readableStreamForRid, + readableStreamForRidUnrefable, + readableStreamForRidUnrefableRef, + readableStreamForRidUnrefableUnref, + ReadableStreamPrototype, + readableStreamThrowIfErrored, + TransformStream, + TransformStreamDefaultController, + WritableStream, + writableStreamClose, + WritableStreamDefaultController, + WritableStreamDefaultWriter, + writableStreamForRid, +}; diff --git a/ext/web/08_text_encoding.js b/ext/web/08_text_encoding.js index 8de7b949f..f3ad966d0 100644 --- a/ext/web/08_text_encoding.js +++ b/ext/web/08_text_encoding.js @@ -9,437 +9,434 @@ /// <reference path="../web/lib.deno_web.d.ts" /> /// <reference lib="esnext" /> -"use strict"; - -((window) => { - const core = Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { - PromiseReject, - PromiseResolve, - // TODO(lucacasonato): add SharedArrayBuffer to primordials - // SharedArrayBufferPrototype - StringPrototypeCharCodeAt, - StringPrototypeSlice, - TypedArrayPrototypeSubarray, - Uint8Array, - ObjectPrototypeIsPrototypeOf, - ArrayBufferIsView, - Uint32Array, - } = window.__bootstrap.primordials; - - class TextDecoder { - /** @type {string} */ - #encoding; - /** @type {boolean} */ - #fatal; - /** @type {boolean} */ - #ignoreBOM; - /** @type {boolean} */ - #utf8SinglePass; - - /** @type {number | null} */ - #rid = null; - - /** - * @param {string} label - * @param {TextDecoderOptions} options - */ - constructor(label = "utf-8", options = {}) { - const prefix = "Failed to construct 'TextDecoder'"; - label = webidl.converters.DOMString(label, { +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + PromiseReject, + PromiseResolve, + // TODO(lucacasonato): add SharedArrayBuffer to primordials + // SharedArrayBufferPrototype + StringPrototypeCharCodeAt, + StringPrototypeSlice, + TypedArrayPrototypeSubarray, + Uint8Array, + ObjectPrototypeIsPrototypeOf, + ArrayBufferIsView, + Uint32Array, +} = primordials; + +class TextDecoder { + /** @type {string} */ + #encoding; + /** @type {boolean} */ + #fatal; + /** @type {boolean} */ + #ignoreBOM; + /** @type {boolean} */ + #utf8SinglePass; + + /** @type {number | null} */ + #rid = null; + + /** + * @param {string} label + * @param {TextDecoderOptions} options + */ + constructor(label = "utf-8", options = {}) { + const prefix = "Failed to construct 'TextDecoder'"; + label = webidl.converters.DOMString(label, { + prefix, + context: "Argument 1", + }); + options = webidl.converters.TextDecoderOptions(options, { + prefix, + context: "Argument 2", + }); + const encoding = ops.op_encoding_normalize_label(label); + this.#encoding = encoding; + this.#fatal = options.fatal; + this.#ignoreBOM = options.ignoreBOM; + this.#utf8SinglePass = encoding === "utf-8" && !options.fatal; + this[webidl.brand] = webidl.brand; + } + + /** @returns {string} */ + get encoding() { + webidl.assertBranded(this, TextDecoderPrototype); + return this.#encoding; + } + + /** @returns {boolean} */ + get fatal() { + webidl.assertBranded(this, TextDecoderPrototype); + return this.#fatal; + } + + /** @returns {boolean} */ + get ignoreBOM() { + webidl.assertBranded(this, TextDecoderPrototype); + return this.#ignoreBOM; + } + + /** + * @param {BufferSource} [input] + * @param {TextDecodeOptions} options + */ + decode(input = new Uint8Array(), options = undefined) { + webidl.assertBranded(this, TextDecoderPrototype); + const prefix = "Failed to execute 'decode' on 'TextDecoder'"; + if (input !== undefined) { + input = webidl.converters.BufferSource(input, { prefix, context: "Argument 1", + allowShared: true, }); - options = webidl.converters.TextDecoderOptions(options, { + } + let stream = false; + if (options !== undefined) { + options = webidl.converters.TextDecodeOptions(options, { prefix, context: "Argument 2", }); - const encoding = ops.op_encoding_normalize_label(label); - this.#encoding = encoding; - this.#fatal = options.fatal; - this.#ignoreBOM = options.ignoreBOM; - this.#utf8SinglePass = encoding === "utf-8" && !options.fatal; - this[webidl.brand] = webidl.brand; + stream = options.stream; } - /** @returns {string} */ - get encoding() { - webidl.assertBranded(this, TextDecoderPrototype); - return this.#encoding; - } - - /** @returns {boolean} */ - get fatal() { - webidl.assertBranded(this, TextDecoderPrototype); - return this.#fatal; - } - - /** @returns {boolean} */ - get ignoreBOM() { - webidl.assertBranded(this, TextDecoderPrototype); - return this.#ignoreBOM; - } - - /** - * @param {BufferSource} [input] - * @param {TextDecodeOptions} options - */ - decode(input = new Uint8Array(), options = undefined) { - webidl.assertBranded(this, TextDecoderPrototype); - const prefix = "Failed to execute 'decode' on 'TextDecoder'"; - if (input !== undefined) { - input = webidl.converters.BufferSource(input, { - prefix, - context: "Argument 1", - allowShared: true, - }); - } - let stream = false; - if (options !== undefined) { - options = webidl.converters.TextDecodeOptions(options, { - prefix, - context: "Argument 2", - }); - stream = options.stream; + try { + // Note from spec: implementations are strongly encouraged to use an implementation strategy that avoids this copy. + // When doing so they will have to make sure that changes to input do not affect future calls to decode(). + if ( + ObjectPrototypeIsPrototypeOf( + // deno-lint-ignore prefer-primordials + SharedArrayBuffer.prototype, + input || input.buffer, + ) + ) { + // We clone the data into a non-shared ArrayBuffer so we can pass it + // to Rust. + // `input` is now a Uint8Array, and calling the TypedArray constructor + // with a TypedArray argument copies the data. + if (ArrayBufferIsView(input)) { + input = new Uint8Array( + input.buffer, + input.byteOffset, + input.byteLength, + ); + } else { + input = new Uint8Array(input); + } } - try { - // Note from spec: implementations are strongly encouraged to use an implementation strategy that avoids this copy. - // When doing so they will have to make sure that changes to input do not affect future calls to decode(). - if ( - ObjectPrototypeIsPrototypeOf( - // deno-lint-ignore prefer-primordials - SharedArrayBuffer.prototype, - input || input.buffer, - ) - ) { - // We clone the data into a non-shared ArrayBuffer so we can pass it - // to Rust. - // `input` is now a Uint8Array, and calling the TypedArray constructor - // with a TypedArray argument copies the data. - if (ArrayBufferIsView(input)) { - input = new Uint8Array( - input.buffer, - input.byteOffset, - input.byteLength, - ); - } else { - input = new Uint8Array(input); - } + // Fast path for single pass encoding. + if (!stream && this.#rid === null) { + // Fast path for utf8 single pass encoding. + if (this.#utf8SinglePass) { + return ops.op_encoding_decode_utf8(input, this.#ignoreBOM); } - // Fast path for single pass encoding. - if (!stream && this.#rid === null) { - // Fast path for utf8 single pass encoding. - if (this.#utf8SinglePass) { - return ops.op_encoding_decode_utf8(input, this.#ignoreBOM); - } - - return ops.op_encoding_decode_single( - input, - this.#encoding, - this.#fatal, - this.#ignoreBOM, - ); - } + return ops.op_encoding_decode_single( + input, + this.#encoding, + this.#fatal, + this.#ignoreBOM, + ); + } - if (this.#rid === null) { - this.#rid = ops.op_encoding_new_decoder( - this.#encoding, - this.#fatal, - this.#ignoreBOM, - ); - } - return ops.op_encoding_decode(input, this.#rid, stream); - } finally { - if (!stream && this.#rid !== null) { - core.close(this.#rid); - this.#rid = null; - } + if (this.#rid === null) { + this.#rid = ops.op_encoding_new_decoder( + this.#encoding, + this.#fatal, + this.#ignoreBOM, + ); + } + return ops.op_encoding_decode(input, this.#rid, stream); + } finally { + if (!stream && this.#rid !== null) { + core.close(this.#rid); + this.#rid = null; } } } +} - webidl.configurePrototype(TextDecoder); - const TextDecoderPrototype = TextDecoder.prototype; +webidl.configurePrototype(TextDecoder); +const TextDecoderPrototype = TextDecoder.prototype; - class TextEncoder { - constructor() { - this[webidl.brand] = webidl.brand; - } +class TextEncoder { + constructor() { + this[webidl.brand] = webidl.brand; + } - /** @returns {string} */ - get encoding() { - webidl.assertBranded(this, TextEncoderPrototype); - return "utf-8"; - } + /** @returns {string} */ + get encoding() { + webidl.assertBranded(this, TextEncoderPrototype); + return "utf-8"; + } - /** - * @param {string} input - * @returns {Uint8Array} - */ - encode(input = "") { - webidl.assertBranded(this, TextEncoderPrototype); - const prefix = "Failed to execute 'encode' on 'TextEncoder'"; - // The WebIDL type of `input` is `USVString`, but `core.encode` already - // converts lone surrogates to the replacement character. - input = webidl.converters.DOMString(input, { - prefix, - context: "Argument 1", - }); - return core.encode(input); - } + /** + * @param {string} input + * @returns {Uint8Array} + */ + encode(input = "") { + webidl.assertBranded(this, TextEncoderPrototype); + const prefix = "Failed to execute 'encode' on 'TextEncoder'"; + // The WebIDL type of `input` is `USVString`, but `core.encode` already + // converts lone surrogates to the replacement character. + input = webidl.converters.DOMString(input, { + prefix, + context: "Argument 1", + }); + return core.encode(input); + } - /** - * @param {string} source - * @param {Uint8Array} destination - * @returns {TextEncoderEncodeIntoResult} - */ - encodeInto(source, destination) { - webidl.assertBranded(this, TextEncoderPrototype); - const prefix = "Failed to execute 'encodeInto' on 'TextEncoder'"; - // The WebIDL type of `source` is `USVString`, but the ops bindings - // already convert lone surrogates to the replacement character. - source = webidl.converters.DOMString(source, { - prefix, - context: "Argument 1", - }); - destination = webidl.converters.Uint8Array(destination, { - prefix, - context: "Argument 2", - allowShared: true, - }); - ops.op_encoding_encode_into(source, destination, encodeIntoBuf); - return { - read: encodeIntoBuf[0], - written: encodeIntoBuf[1], - }; - } + /** + * @param {string} source + * @param {Uint8Array} destination + * @returns {TextEncoderEncodeIntoResult} + */ + encodeInto(source, destination) { + webidl.assertBranded(this, TextEncoderPrototype); + const prefix = "Failed to execute 'encodeInto' on 'TextEncoder'"; + // The WebIDL type of `source` is `USVString`, but the ops bindings + // already convert lone surrogates to the replacement character. + source = webidl.converters.DOMString(source, { + prefix, + context: "Argument 1", + }); + destination = webidl.converters.Uint8Array(destination, { + prefix, + context: "Argument 2", + allowShared: true, + }); + ops.op_encoding_encode_into(source, destination, encodeIntoBuf); + return { + read: encodeIntoBuf[0], + written: encodeIntoBuf[1], + }; } +} - const encodeIntoBuf = new Uint32Array(2); +const encodeIntoBuf = new Uint32Array(2); - webidl.configurePrototype(TextEncoder); - const TextEncoderPrototype = TextEncoder.prototype; +webidl.configurePrototype(TextEncoder); +const TextEncoderPrototype = TextEncoder.prototype; - class TextDecoderStream { - /** @type {TextDecoder} */ - #decoder; - /** @type {TransformStream<BufferSource, string>} */ - #transform; +class TextDecoderStream { + /** @type {TextDecoder} */ + #decoder; + /** @type {TransformStream<BufferSource, string>} */ + #transform; - /** - * @param {string} label - * @param {TextDecoderOptions} options - */ - constructor(label = "utf-8", options = {}) { - const prefix = "Failed to construct 'TextDecoderStream'"; - label = webidl.converters.DOMString(label, { - prefix, - context: "Argument 1", - }); - options = webidl.converters.TextDecoderOptions(options, { - prefix, - context: "Argument 2", - }); - this.#decoder = new TextDecoder(label, options); - this.#transform = new TransformStream({ - // The transform and flush functions need access to TextDecoderStream's - // `this`, so they are defined as functions rather than methods. - transform: (chunk, controller) => { - try { - chunk = webidl.converters.BufferSource(chunk, { - allowShared: true, - }); - const decoded = this.#decoder.decode(chunk, { stream: true }); - if (decoded) { - controller.enqueue(decoded); - } - return PromiseResolve(); - } catch (err) { - return PromiseReject(err); + /** + * @param {string} label + * @param {TextDecoderOptions} options + */ + constructor(label = "utf-8", options = {}) { + const prefix = "Failed to construct 'TextDecoderStream'"; + label = webidl.converters.DOMString(label, { + prefix, + context: "Argument 1", + }); + options = webidl.converters.TextDecoderOptions(options, { + prefix, + context: "Argument 2", + }); + this.#decoder = new TextDecoder(label, options); + this.#transform = new TransformStream({ + // The transform and flush functions need access to TextDecoderStream's + // `this`, so they are defined as functions rather than methods. + transform: (chunk, controller) => { + try { + chunk = webidl.converters.BufferSource(chunk, { + allowShared: true, + }); + const decoded = this.#decoder.decode(chunk, { stream: true }); + if (decoded) { + controller.enqueue(decoded); } - }, - flush: (controller) => { - try { - const final = this.#decoder.decode(); - if (final) { - controller.enqueue(final); - } - return PromiseResolve(); - } catch (err) { - return PromiseReject(err); + return PromiseResolve(); + } catch (err) { + return PromiseReject(err); + } + }, + flush: (controller) => { + try { + const final = this.#decoder.decode(); + if (final) { + controller.enqueue(final); } - }, - }); - this[webidl.brand] = webidl.brand; - } - - /** @returns {string} */ - get encoding() { - webidl.assertBranded(this, TextDecoderStreamPrototype); - return this.#decoder.encoding; - } + return PromiseResolve(); + } catch (err) { + return PromiseReject(err); + } + }, + }); + this[webidl.brand] = webidl.brand; + } - /** @returns {boolean} */ - get fatal() { - webidl.assertBranded(this, TextDecoderStreamPrototype); - return this.#decoder.fatal; - } + /** @returns {string} */ + get encoding() { + webidl.assertBranded(this, TextDecoderStreamPrototype); + return this.#decoder.encoding; + } - /** @returns {boolean} */ - get ignoreBOM() { - webidl.assertBranded(this, TextDecoderStreamPrototype); - return this.#decoder.ignoreBOM; - } + /** @returns {boolean} */ + get fatal() { + webidl.assertBranded(this, TextDecoderStreamPrototype); + return this.#decoder.fatal; + } - /** @returns {ReadableStream<string>} */ - get readable() { - webidl.assertBranded(this, TextDecoderStreamPrototype); - return this.#transform.readable; - } + /** @returns {boolean} */ + get ignoreBOM() { + webidl.assertBranded(this, TextDecoderStreamPrototype); + return this.#decoder.ignoreBOM; + } - /** @returns {WritableStream<BufferSource>} */ - get writable() { - webidl.assertBranded(this, TextDecoderStreamPrototype); - return this.#transform.writable; - } + /** @returns {ReadableStream<string>} */ + get readable() { + webidl.assertBranded(this, TextDecoderStreamPrototype); + return this.#transform.readable; } - webidl.configurePrototype(TextDecoderStream); - const TextDecoderStreamPrototype = TextDecoderStream.prototype; - - class TextEncoderStream { - /** @type {string | null} */ - #pendingHighSurrogate = null; - /** @type {TransformStream<string, Uint8Array>} */ - #transform; - - constructor() { - this.#transform = new TransformStream({ - // The transform and flush functions need access to TextEncoderStream's - // `this`, so they are defined as functions rather than methods. - transform: (chunk, controller) => { - try { - chunk = webidl.converters.DOMString(chunk); - if (chunk === "") { - return PromiseResolve(); - } - if (this.#pendingHighSurrogate !== null) { - chunk = this.#pendingHighSurrogate + chunk; - } - const lastCodeUnit = StringPrototypeCharCodeAt( - chunk, - chunk.length - 1, - ); - if (0xD800 <= lastCodeUnit && lastCodeUnit <= 0xDBFF) { - this.#pendingHighSurrogate = StringPrototypeSlice(chunk, -1); - chunk = StringPrototypeSlice(chunk, 0, -1); - } else { - this.#pendingHighSurrogate = null; - } - if (chunk) { - controller.enqueue(core.encode(chunk)); - } + /** @returns {WritableStream<BufferSource>} */ + get writable() { + webidl.assertBranded(this, TextDecoderStreamPrototype); + return this.#transform.writable; + } +} + +webidl.configurePrototype(TextDecoderStream); +const TextDecoderStreamPrototype = TextDecoderStream.prototype; + +class TextEncoderStream { + /** @type {string | null} */ + #pendingHighSurrogate = null; + /** @type {TransformStream<string, Uint8Array>} */ + #transform; + + constructor() { + this.#transform = new TransformStream({ + // The transform and flush functions need access to TextEncoderStream's + // `this`, so they are defined as functions rather than methods. + transform: (chunk, controller) => { + try { + chunk = webidl.converters.DOMString(chunk); + if (chunk === "") { return PromiseResolve(); - } catch (err) { - return PromiseReject(err); } - }, - flush: (controller) => { - try { - if (this.#pendingHighSurrogate !== null) { - controller.enqueue(new Uint8Array([0xEF, 0xBF, 0xBD])); - } - return PromiseResolve(); - } catch (err) { - return PromiseReject(err); + if (this.#pendingHighSurrogate !== null) { + chunk = this.#pendingHighSurrogate + chunk; } - }, - }); - this[webidl.brand] = webidl.brand; - } - - /** @returns {string} */ - get encoding() { - webidl.assertBranded(this, TextEncoderStreamPrototype); - return "utf-8"; - } - - /** @returns {ReadableStream<Uint8Array>} */ - get readable() { - webidl.assertBranded(this, TextEncoderStreamPrototype); - return this.#transform.readable; - } - - /** @returns {WritableStream<string>} */ - get writable() { - webidl.assertBranded(this, TextEncoderStreamPrototype); - return this.#transform.writable; - } - } - - webidl.configurePrototype(TextEncoderStream); - const TextEncoderStreamPrototype = TextEncoderStream.prototype; - - webidl.converters.TextDecoderOptions = webidl.createDictionaryConverter( - "TextDecoderOptions", - [ - { - key: "fatal", - converter: webidl.converters.boolean, - defaultValue: false, - }, - { - key: "ignoreBOM", - converter: webidl.converters.boolean, - defaultValue: false, + const lastCodeUnit = StringPrototypeCharCodeAt( + chunk, + chunk.length - 1, + ); + if (0xD800 <= lastCodeUnit && lastCodeUnit <= 0xDBFF) { + this.#pendingHighSurrogate = StringPrototypeSlice(chunk, -1); + chunk = StringPrototypeSlice(chunk, 0, -1); + } else { + this.#pendingHighSurrogate = null; + } + if (chunk) { + controller.enqueue(core.encode(chunk)); + } + return PromiseResolve(); + } catch (err) { + return PromiseReject(err); + } }, - ], - ); - webidl.converters.TextDecodeOptions = webidl.createDictionaryConverter( - "TextDecodeOptions", - [ - { - key: "stream", - converter: webidl.converters.boolean, - defaultValue: false, + flush: (controller) => { + try { + if (this.#pendingHighSurrogate !== null) { + controller.enqueue(new Uint8Array([0xEF, 0xBF, 0xBD])); + } + return PromiseResolve(); + } catch (err) { + return PromiseReject(err); + } }, - ], - ); + }); + this[webidl.brand] = webidl.brand; + } - /** - * @param {Uint8Array} bytes - */ - function decode(bytes, encoding) { - const BOMEncoding = BOMSniff(bytes); - if (BOMEncoding !== null) { - encoding = BOMEncoding; - const start = BOMEncoding === "UTF-8" ? 3 : 2; - bytes = TypedArrayPrototypeSubarray(bytes, start); - } - return new TextDecoder(encoding).decode(bytes); + /** @returns {string} */ + get encoding() { + webidl.assertBranded(this, TextEncoderStreamPrototype); + return "utf-8"; } - /** - * @param {Uint8Array} bytes - */ - function BOMSniff(bytes) { - if (bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) { - return "UTF-8"; - } - if (bytes[0] === 0xFE && bytes[1] === 0xFF) return "UTF-16BE"; - if (bytes[0] === 0xFF && bytes[1] === 0xFE) return "UTF-16LE"; - return null; + /** @returns {ReadableStream<Uint8Array>} */ + get readable() { + webidl.assertBranded(this, TextEncoderStreamPrototype); + return this.#transform.readable; } - window.__bootstrap.encoding = { - TextEncoder, - TextDecoder, - TextEncoderStream, - TextDecoderStream, - decode, - }; -})(this); + /** @returns {WritableStream<string>} */ + get writable() { + webidl.assertBranded(this, TextEncoderStreamPrototype); + return this.#transform.writable; + } +} + +webidl.configurePrototype(TextEncoderStream); +const TextEncoderStreamPrototype = TextEncoderStream.prototype; + +webidl.converters.TextDecoderOptions = webidl.createDictionaryConverter( + "TextDecoderOptions", + [ + { + key: "fatal", + converter: webidl.converters.boolean, + defaultValue: false, + }, + { + key: "ignoreBOM", + converter: webidl.converters.boolean, + defaultValue: false, + }, + ], +); +webidl.converters.TextDecodeOptions = webidl.createDictionaryConverter( + "TextDecodeOptions", + [ + { + key: "stream", + converter: webidl.converters.boolean, + defaultValue: false, + }, + ], +); + +/** + * @param {Uint8Array} bytes + */ +function decode(bytes, encoding) { + const BOMEncoding = BOMSniff(bytes); + if (BOMEncoding !== null) { + encoding = BOMEncoding; + const start = BOMEncoding === "UTF-8" ? 3 : 2; + bytes = TypedArrayPrototypeSubarray(bytes, start); + } + return new TextDecoder(encoding).decode(bytes); +} + +/** + * @param {Uint8Array} bytes + */ +function BOMSniff(bytes) { + if (bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) { + return "UTF-8"; + } + if (bytes[0] === 0xFE && bytes[1] === 0xFF) return "UTF-16BE"; + if (bytes[0] === 0xFF && bytes[1] === 0xFE) return "UTF-16LE"; + return null; +} + +export { + decode, + TextDecoder, + TextDecoderStream, + TextEncoder, + TextEncoderStream, +}; diff --git a/ext/web/09_file.js b/ext/web/09_file.js index ecdce3e6a..e1be3b4c2 100644 --- a/ext/web/09_file.js +++ b/ext/web/09_file.js @@ -9,630 +9,628 @@ /// <reference path="../web/lib.deno_web.d.ts" /> /// <reference path="./internal.d.ts" /> /// <reference lib="esnext" /> -"use strict"; - -((window) => { - const core = window.Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { - ArrayBufferPrototype, - ArrayBufferPrototypeSlice, - ArrayBufferIsView, - ArrayPrototypePush, - AsyncGeneratorPrototypeNext, - Date, - DatePrototypeGetTime, - FinalizationRegistry, - MathMax, - MathMin, - ObjectPrototypeIsPrototypeOf, - RegExpPrototypeTest, - // TODO(lucacasonato): add SharedArrayBuffer to primordials - // SharedArrayBufferPrototype - StringPrototypeCharAt, - StringPrototypeToLowerCase, - StringPrototypeSlice, - Symbol, - SymbolFor, - TypedArrayPrototypeSet, - TypeError, - Uint8Array, - } = window.__bootstrap.primordials; - const consoleInternal = window.__bootstrap.console; - - // TODO(lucacasonato): this needs to not be hardcoded and instead depend on - // host os. - const isWindows = false; - /** - * @param {string} input - * @param {number} position - * @returns {{result: string, position: number}} - */ - function collectCodepointsNotCRLF(input, position) { - // See https://w3c.github.io/FileAPI/#convert-line-endings-to-native and - // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points - const start = position; - for ( - let c = StringPrototypeCharAt(input, position); - position < input.length && !(c === "\r" || c === "\n"); - c = StringPrototypeCharAt(input, ++position) +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayBufferPrototype, + ArrayBufferPrototypeSlice, + ArrayBufferIsView, + ArrayPrototypePush, + AsyncGeneratorPrototypeNext, + Date, + DatePrototypeGetTime, + FinalizationRegistry, + MathMax, + MathMin, + ObjectPrototypeIsPrototypeOf, + RegExpPrototypeTest, + // TODO(lucacasonato): add SharedArrayBuffer to primordials + // SharedArrayBufferPrototype + StringPrototypeCharAt, + StringPrototypeToLowerCase, + StringPrototypeSlice, + Symbol, + SymbolFor, + TypedArrayPrototypeSet, + TypeError, + Uint8Array, +} = primordials; +import { createFilteredInspectProxy } from "internal:ext/console/02_console.js"; + +// TODO(lucacasonato): this needs to not be hardcoded and instead depend on +// host os. +const isWindows = false; + +/** + * @param {string} input + * @param {number} position + * @returns {{result: string, position: number}} + */ +function collectCodepointsNotCRLF(input, position) { + // See https://w3c.github.io/FileAPI/#convert-line-endings-to-native and + // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points + const start = position; + for ( + let c = StringPrototypeCharAt(input, position); + position < input.length && !(c === "\r" || c === "\n"); + c = StringPrototypeCharAt(input, ++position) + ); + return { result: StringPrototypeSlice(input, start, position), position }; +} + +/** + * @param {string} s + * @returns {string} + */ +function convertLineEndingsToNative(s) { + const nativeLineEnding = isWindows ? "\r\n" : "\n"; + + let { result, position } = collectCodepointsNotCRLF(s, 0); + + while (position < s.length) { + const codePoint = StringPrototypeCharAt(s, position); + if (codePoint === "\r") { + result += nativeLineEnding; + position++; + if ( + position < s.length && StringPrototypeCharAt(s, position) === "\n" + ) { + position++; + } + } else if (codePoint === "\n") { + position++; + result += nativeLineEnding; + } + const { result: token, position: newPosition } = collectCodepointsNotCRLF( + s, + position, ); - return { result: StringPrototypeSlice(input, start, position), position }; + position = newPosition; + result += token; } - /** - * @param {string} s - * @returns {string} - */ - function convertLineEndingsToNative(s) { - const nativeLineEnding = isWindows ? "\r\n" : "\n"; - - let { result, position } = collectCodepointsNotCRLF(s, 0); + return result; +} - while (position < s.length) { - const codePoint = StringPrototypeCharAt(s, position); - if (codePoint === "\r") { - result += nativeLineEnding; - position++; - if ( - position < s.length && StringPrototypeCharAt(s, position) === "\n" - ) { - position++; - } - } else if (codePoint === "\n") { - position++; - result += nativeLineEnding; - } - const { result: token, position: newPosition } = collectCodepointsNotCRLF( - s, - position, +/** @param {(BlobReference | Blob)[]} parts */ +async function* toIterator(parts) { + for (let i = 0; i < parts.length; ++i) { + yield* parts[i].stream(); + } +} + +/** @typedef {BufferSource | Blob | string} BlobPart */ + +/** + * @param {BlobPart[]} parts + * @param {string} endings + * @returns {{ parts: (BlobReference|Blob)[], size: number }} + */ +function processBlobParts(parts, endings) { + /** @type {(BlobReference|Blob)[]} */ + const processedParts = []; + let size = 0; + for (let i = 0; i < parts.length; ++i) { + const element = parts[i]; + if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, element)) { + const chunk = new Uint8Array(ArrayBufferPrototypeSlice(element, 0)); + ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); + size += element.byteLength; + } else if (ArrayBufferIsView(element)) { + const chunk = new Uint8Array( + element.buffer, + element.byteOffset, + element.byteLength, ); - position = newPosition; - result += token; + size += element.byteLength; + ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); + } else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, element)) { + ArrayPrototypePush(processedParts, element); + size += element.size; + } else if (typeof element === "string") { + const chunk = core.encode( + endings == "native" ? convertLineEndingsToNative(element) : element, + ); + size += chunk.byteLength; + ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); + } else { + throw new TypeError("Unreachable code (invalid element type)"); } - - return result; } - - /** @param {(BlobReference | Blob)[]} parts */ - async function* toIterator(parts) { - for (let i = 0; i < parts.length; ++i) { - yield* parts[i].stream(); + return { parts: processedParts, size }; +} + +/** + * @param {string} str + * @returns {string} + */ +function normalizeType(str) { + let normalizedType = str; + if (!RegExpPrototypeTest(/^[\x20-\x7E]*$/, str)) { + normalizedType = ""; + } + return StringPrototypeToLowerCase(normalizedType); +} + +/** + * Get all Parts as a flat array containing all references + * @param {Blob} blob + * @param {string[]} bag + * @returns {string[]} + */ +function getParts(blob, bag = []) { + const parts = blob[_parts]; + for (let i = 0; i < parts.length; ++i) { + const part = parts[i]; + if (ObjectPrototypeIsPrototypeOf(BlobPrototype, part)) { + getParts(part, bag); + } else { + ArrayPrototypePush(bag, part._id); } } + return bag; +} - /** @typedef {BufferSource | Blob | string} BlobPart */ +const _type = Symbol("Type"); +const _size = Symbol("Size"); +const _parts = Symbol("Parts"); + +class Blob { + [_type] = ""; + [_size] = 0; + [_parts]; /** - * @param {BlobPart[]} parts - * @param {string} endings - * @returns {{ parts: (BlobReference|Blob)[], size: number }} + * @param {BlobPart[]} blobParts + * @param {BlobPropertyBag} options */ - function processBlobParts(parts, endings) { - /** @type {(BlobReference|Blob)[]} */ - const processedParts = []; - let size = 0; - for (let i = 0; i < parts.length; ++i) { - const element = parts[i]; - if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, element)) { - const chunk = new Uint8Array(ArrayBufferPrototypeSlice(element, 0)); - ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); - size += element.byteLength; - } else if (ArrayBufferIsView(element)) { - const chunk = new Uint8Array( - element.buffer, - element.byteOffset, - element.byteLength, - ); - size += element.byteLength; - ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); - } else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, element)) { - ArrayPrototypePush(processedParts, element); - size += element.size; - } else if (typeof element === "string") { - const chunk = core.encode( - endings == "native" ? convertLineEndingsToNative(element) : element, - ); - size += chunk.byteLength; - ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); - } else { - throw new TypeError("Unreachable code (invalid element type)"); - } - } - return { parts: processedParts, size }; + constructor(blobParts = [], options = {}) { + const prefix = "Failed to construct 'Blob'"; + blobParts = webidl.converters["sequence<BlobPart>"](blobParts, { + context: "Argument 1", + prefix, + }); + options = webidl.converters["BlobPropertyBag"](options, { + context: "Argument 2", + prefix, + }); + + this[webidl.brand] = webidl.brand; + + const { parts, size } = processBlobParts( + blobParts, + options.endings, + ); + + this[_parts] = parts; + this[_size] = size; + this[_type] = normalizeType(options.type); } - /** - * @param {string} str - * @returns {string} - */ - function normalizeType(str) { - let normalizedType = str; - if (!RegExpPrototypeTest(/^[\x20-\x7E]*$/, str)) { - normalizedType = ""; - } - return StringPrototypeToLowerCase(normalizedType); + /** @returns {number} */ + get size() { + webidl.assertBranded(this, BlobPrototype); + return this[_size]; } - /** - * Get all Parts as a flat array containing all references - * @param {Blob} blob - * @param {string[]} bag - * @returns {string[]} - */ - function getParts(blob, bag = []) { - const parts = blob[_parts]; - for (let i = 0; i < parts.length; ++i) { - const part = parts[i]; - if (ObjectPrototypeIsPrototypeOf(BlobPrototype, part)) { - getParts(part, bag); - } else { - ArrayPrototypePush(bag, part._id); - } - } - return bag; + /** @returns {string} */ + get type() { + webidl.assertBranded(this, BlobPrototype); + return this[_type]; } - const _type = Symbol("Type"); - const _size = Symbol("Size"); - const _parts = Symbol("Parts"); - - class Blob { - [_type] = ""; - [_size] = 0; - [_parts]; - - /** - * @param {BlobPart[]} blobParts - * @param {BlobPropertyBag} options - */ - constructor(blobParts = [], options = {}) { - const prefix = "Failed to construct 'Blob'"; - blobParts = webidl.converters["sequence<BlobPart>"](blobParts, { + /** + * @param {number} [start] + * @param {number} [end] + * @param {string} [contentType] + * @returns {Blob} + */ + slice(start = undefined, end = undefined, contentType = undefined) { + webidl.assertBranded(this, BlobPrototype); + const prefix = "Failed to execute 'slice' on 'Blob'"; + if (start !== undefined) { + start = webidl.converters["long long"](start, { + clamp: true, context: "Argument 1", prefix, }); - options = webidl.converters["BlobPropertyBag"](options, { + } + if (end !== undefined) { + end = webidl.converters["long long"](end, { + clamp: true, context: "Argument 2", prefix, }); - - this[webidl.brand] = webidl.brand; - - const { parts, size } = processBlobParts( - blobParts, - options.endings, - ); - - this[_parts] = parts; - this[_size] = size; - this[_type] = normalizeType(options.type); } - - /** @returns {number} */ - get size() { - webidl.assertBranded(this, BlobPrototype); - return this[_size]; - } - - /** @returns {string} */ - get type() { - webidl.assertBranded(this, BlobPrototype); - return this[_type]; + if (contentType !== undefined) { + contentType = webidl.converters["DOMString"](contentType, { + context: "Argument 3", + prefix, + }); } - /** - * @param {number} [start] - * @param {number} [end] - * @param {string} [contentType] - * @returns {Blob} - */ - slice(start = undefined, end = undefined, contentType = undefined) { - webidl.assertBranded(this, BlobPrototype); - const prefix = "Failed to execute 'slice' on 'Blob'"; - if (start !== undefined) { - start = webidl.converters["long long"](start, { - clamp: true, - context: "Argument 1", - prefix, - }); - } - if (end !== undefined) { - end = webidl.converters["long long"](end, { - clamp: true, - context: "Argument 2", - prefix, - }); - } - if (contentType !== undefined) { - contentType = webidl.converters["DOMString"](contentType, { - context: "Argument 3", - prefix, - }); - } - - // deno-lint-ignore no-this-alias - const O = this; - /** @type {number} */ - let relativeStart; - if (start === undefined) { - relativeStart = 0; + // deno-lint-ignore no-this-alias + const O = this; + /** @type {number} */ + let relativeStart; + if (start === undefined) { + relativeStart = 0; + } else { + if (start < 0) { + relativeStart = MathMax(O.size + start, 0); } else { - if (start < 0) { - relativeStart = MathMax(O.size + start, 0); - } else { - relativeStart = MathMin(start, O.size); - } + relativeStart = MathMin(start, O.size); } - /** @type {number} */ - let relativeEnd; - if (end === undefined) { - relativeEnd = O.size; + } + /** @type {number} */ + let relativeEnd; + if (end === undefined) { + relativeEnd = O.size; + } else { + if (end < 0) { + relativeEnd = MathMax(O.size + end, 0); } else { - if (end < 0) { - relativeEnd = MathMax(O.size + end, 0); - } else { - relativeEnd = MathMin(end, O.size); - } + relativeEnd = MathMin(end, O.size); } + } - const span = MathMax(relativeEnd - relativeStart, 0); - const blobParts = []; - let added = 0; - - const parts = this[_parts]; - for (let i = 0; i < parts.length; ++i) { - const part = parts[i]; - // don't add the overflow to new blobParts - if (added >= span) { - // Could maybe be possible to remove variable `added` - // and only use relativeEnd? - break; - } - const size = part.size; - if (relativeStart && size <= relativeStart) { - // Skip the beginning and change the relative - // start & end position as we skip the unwanted parts - relativeStart -= size; - relativeEnd -= size; - } else { - const chunk = part.slice( - relativeStart, - MathMin(part.size, relativeEnd), - ); - added += chunk.size; - relativeEnd -= part.size; - ArrayPrototypePush(blobParts, chunk); - relativeStart = 0; // All next sequential parts should start at 0 - } - } + const span = MathMax(relativeEnd - relativeStart, 0); + const blobParts = []; + let added = 0; - /** @type {string} */ - let relativeContentType; - if (contentType === undefined) { - relativeContentType = ""; + const parts = this[_parts]; + for (let i = 0; i < parts.length; ++i) { + const part = parts[i]; + // don't add the overflow to new blobParts + if (added >= span) { + // Could maybe be possible to remove variable `added` + // and only use relativeEnd? + break; + } + const size = part.size; + if (relativeStart && size <= relativeStart) { + // Skip the beginning and change the relative + // start & end position as we skip the unwanted parts + relativeStart -= size; + relativeEnd -= size; } else { - relativeContentType = normalizeType(contentType); + const chunk = part.slice( + relativeStart, + MathMin(part.size, relativeEnd), + ); + added += chunk.size; + relativeEnd -= part.size; + ArrayPrototypePush(blobParts, chunk); + relativeStart = 0; // All next sequential parts should start at 0 } - - const blob = new Blob([], { type: relativeContentType }); - blob[_parts] = blobParts; - blob[_size] = span; - return blob; } - /** - * @returns {ReadableStream<Uint8Array>} - */ - stream() { - webidl.assertBranded(this, BlobPrototype); - const partIterator = toIterator(this[_parts]); - const stream = new ReadableStream({ - type: "bytes", - /** @param {ReadableByteStreamController} controller */ - async pull(controller) { - while (true) { - const { value, done } = await AsyncGeneratorPrototypeNext( - partIterator, - ); - if (done) return controller.close(); - if (value.byteLength > 0) { - return controller.enqueue(value); - } - } - }, - }); - return stream; + /** @type {string} */ + let relativeContentType; + if (contentType === undefined) { + relativeContentType = ""; + } else { + relativeContentType = normalizeType(contentType); } - /** - * @returns {Promise<string>} - */ - async text() { - webidl.assertBranded(this, BlobPrototype); - const buffer = await this.#u8Array(this.size); - return core.decode(buffer); - } + const blob = new Blob([], { type: relativeContentType }); + blob[_parts] = blobParts; + blob[_size] = span; + return blob; + } - async #u8Array(size) { - const bytes = new Uint8Array(size); - const partIterator = toIterator(this[_parts]); - let offset = 0; - while (true) { - const { value, done } = await AsyncGeneratorPrototypeNext( - partIterator, - ); - if (done) break; - const byteLength = value.byteLength; - if (byteLength > 0) { - TypedArrayPrototypeSet(bytes, value, offset); - offset += byteLength; + /** + * @returns {ReadableStream<Uint8Array>} + */ + stream() { + webidl.assertBranded(this, BlobPrototype); + const partIterator = toIterator(this[_parts]); + const stream = new ReadableStream({ + type: "bytes", + /** @param {ReadableByteStreamController} controller */ + async pull(controller) { + while (true) { + const { value, done } = await AsyncGeneratorPrototypeNext( + partIterator, + ); + if (done) return controller.close(); + if (value.byteLength > 0) { + return controller.enqueue(value); + } } - } - return bytes; - } - - /** - * @returns {Promise<ArrayBuffer>} - */ - async arrayBuffer() { - webidl.assertBranded(this, BlobPrototype); - const buf = await this.#u8Array(this.size); - return buf.buffer; - } - - [SymbolFor("Deno.customInspect")](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(BlobPrototype, this), - keys: [ - "size", - "type", - ], - })); - } + }, + }); + return stream; } - webidl.configurePrototype(Blob); - const BlobPrototype = Blob.prototype; + /** + * @returns {Promise<string>} + */ + async text() { + webidl.assertBranded(this, BlobPrototype); + const buffer = await this.#u8Array(this.size); + return core.decode(buffer); + } - webidl.converters["Blob"] = webidl.createInterfaceConverter( - "Blob", - Blob.prototype, - ); - webidl.converters["BlobPart"] = (V, opts) => { - // Union for ((ArrayBuffer or ArrayBufferView) or Blob or USVString) - if (typeof V == "object") { - if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) { - return webidl.converters["Blob"](V, opts); - } - if ( - ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) || - // deno-lint-ignore prefer-primordials - ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V) - ) { - return webidl.converters["ArrayBuffer"](V, opts); - } - if (ArrayBufferIsView(V)) { - return webidl.converters["ArrayBufferView"](V, opts); + async #u8Array(size) { + const bytes = new Uint8Array(size); + const partIterator = toIterator(this[_parts]); + let offset = 0; + while (true) { + const { value, done } = await AsyncGeneratorPrototypeNext( + partIterator, + ); + if (done) break; + const byteLength = value.byteLength; + if (byteLength > 0) { + TypedArrayPrototypeSet(bytes, value, offset); + offset += byteLength; } } - // BlobPart is passed to processBlobParts after conversion, which calls core.encode() - // on the string. - // core.encode() is equivalent to USVString normalization. - return webidl.converters["DOMString"](V, opts); - }; - webidl.converters["sequence<BlobPart>"] = webidl.createSequenceConverter( - webidl.converters["BlobPart"], - ); - webidl.converters["EndingType"] = webidl.createEnumConverter("EndingType", [ - "transparent", - "native", - ]); - const blobPropertyBagDictionary = [ - { - key: "type", - converter: webidl.converters["DOMString"], - defaultValue: "", - }, - { - key: "endings", - converter: webidl.converters["EndingType"], - defaultValue: "transparent", - }, - ]; - webidl.converters["BlobPropertyBag"] = webidl.createDictionaryConverter( - "BlobPropertyBag", - blobPropertyBagDictionary, - ); - - const _Name = Symbol("[[Name]]"); - const _LastModified = Symbol("[[LastModified]]"); - - class File extends Blob { - /** @type {string} */ - [_Name]; - /** @type {number} */ - [_LastModified]; - - /** - * @param {BlobPart[]} fileBits - * @param {string} fileName - * @param {FilePropertyBag} options - */ - constructor(fileBits, fileName, options = {}) { - const prefix = "Failed to construct 'File'"; - webidl.requiredArguments(arguments.length, 2, { prefix }); - - fileBits = webidl.converters["sequence<BlobPart>"](fileBits, { - context: "Argument 1", - prefix, - }); - fileName = webidl.converters["USVString"](fileName, { - context: "Argument 2", - prefix, - }); - options = webidl.converters["FilePropertyBag"](options, { - context: "Argument 3", - prefix, - }); + return bytes; + } - super(fileBits, options); + /** + * @returns {Promise<ArrayBuffer>} + */ + async arrayBuffer() { + webidl.assertBranded(this, BlobPrototype); + const buf = await this.#u8Array(this.size); + return buf.buffer; + } - /** @type {string} */ - this[_Name] = fileName; - if (options.lastModified === undefined) { - /** @type {number} */ - this[_LastModified] = DatePrototypeGetTime(new Date()); - } else { - /** @type {number} */ - this[_LastModified] = options.lastModified; - } + [SymbolFor("Deno.customInspect")](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(BlobPrototype, this), + keys: [ + "size", + "type", + ], + })); + } +} + +webidl.configurePrototype(Blob); +const BlobPrototype = Blob.prototype; + +webidl.converters["Blob"] = webidl.createInterfaceConverter( + "Blob", + Blob.prototype, +); +webidl.converters["BlobPart"] = (V, opts) => { + // Union for ((ArrayBuffer or ArrayBufferView) or Blob or USVString) + if (typeof V == "object") { + if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) { + return webidl.converters["Blob"](V, opts); } - - /** @returns {string} */ - get name() { - webidl.assertBranded(this, FilePrototype); - return this[_Name]; + if ( + ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) || + // deno-lint-ignore prefer-primordials + ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V) + ) { + return webidl.converters["ArrayBuffer"](V, opts); } - - /** @returns {number} */ - get lastModified() { - webidl.assertBranded(this, FilePrototype); - return this[_LastModified]; + if (ArrayBufferIsView(V)) { + return webidl.converters["ArrayBufferView"](V, opts); } } - - webidl.configurePrototype(File); - const FilePrototype = File.prototype; - - webidl.converters["FilePropertyBag"] = webidl.createDictionaryConverter( - "FilePropertyBag", - blobPropertyBagDictionary, - [ - { - key: "lastModified", - converter: webidl.converters["long long"], - }, - ], - ); - - // A finalization registry to deallocate a blob part when its JS reference is - // garbage collected. - const registry = new FinalizationRegistry((uuid) => { - ops.op_blob_remove_part(uuid); - }); - - // TODO(lucacasonato): get a better stream from Rust in BlobReference#stream + // BlobPart is passed to processBlobParts after conversion, which calls core.encode() + // on the string. + // core.encode() is equivalent to USVString normalization. + return webidl.converters["DOMString"](V, opts); +}; +webidl.converters["sequence<BlobPart>"] = webidl.createSequenceConverter( + webidl.converters["BlobPart"], +); +webidl.converters["EndingType"] = webidl.createEnumConverter("EndingType", [ + "transparent", + "native", +]); +const blobPropertyBagDictionary = [ + { + key: "type", + converter: webidl.converters["DOMString"], + defaultValue: "", + }, + { + key: "endings", + converter: webidl.converters["EndingType"], + defaultValue: "transparent", + }, +]; +webidl.converters["BlobPropertyBag"] = webidl.createDictionaryConverter( + "BlobPropertyBag", + blobPropertyBagDictionary, +); + +const _Name = Symbol("[[Name]]"); +const _LastModified = Symbol("[[LastModified]]"); + +class File extends Blob { + /** @type {string} */ + [_Name]; + /** @type {number} */ + [_LastModified]; /** - * An opaque reference to a blob part in Rust. This could be backed by a file, - * in memory storage, or something else. + * @param {BlobPart[]} fileBits + * @param {string} fileName + * @param {FilePropertyBag} options */ - class BlobReference { - /** - * Don't use directly. Use `BlobReference.fromUint8Array`. - * @param {string} id - * @param {number} size - */ - constructor(id, size) { - this._id = id; - this.size = size; - registry.register(this, id); - } + constructor(fileBits, fileName, options = {}) { + const prefix = "Failed to construct 'File'"; + webidl.requiredArguments(arguments.length, 2, { prefix }); + + fileBits = webidl.converters["sequence<BlobPart>"](fileBits, { + context: "Argument 1", + prefix, + }); + fileName = webidl.converters["USVString"](fileName, { + context: "Argument 2", + prefix, + }); + options = webidl.converters["FilePropertyBag"](options, { + context: "Argument 3", + prefix, + }); + + super(fileBits, options); - /** - * Create a new blob part from a Uint8Array. - * - * @param {Uint8Array} data - * @returns {BlobReference} - */ - static fromUint8Array(data) { - const id = ops.op_blob_create_part(data); - return new BlobReference(id, data.byteLength); + /** @type {string} */ + this[_Name] = fileName; + if (options.lastModified === undefined) { + /** @type {number} */ + this[_LastModified] = DatePrototypeGetTime(new Date()); + } else { + /** @type {number} */ + this[_LastModified] = options.lastModified; } + } - /** - * Create a new BlobReference by slicing this BlobReference. This is a copy - * free operation - the sliced reference will still reference the original - * underlying bytes. - * - * @param {number} start - * @param {number} end - * @returns {BlobReference} - */ - slice(start, end) { - const size = end - start; - const id = ops.op_blob_slice_part(this._id, { - start, - len: size, - }); - return new BlobReference(id, size); - } + /** @returns {string} */ + get name() { + webidl.assertBranded(this, FilePrototype); + return this[_Name]; + } - /** - * Read the entire contents of the reference blob. - * @returns {AsyncGenerator<Uint8Array>} - */ - async *stream() { - yield core.opAsync("op_blob_read_part", this._id); - - // let position = 0; - // const end = this.size; - // while (position !== end) { - // const size = MathMin(end - position, 65536); - // const chunk = this.slice(position, position + size); - // position += chunk.size; - // yield core.opAsync("op_blob_read_part", chunk._id); - // } - } + /** @returns {number} */ + get lastModified() { + webidl.assertBranded(this, FilePrototype); + return this[_LastModified]; } +} +webidl.configurePrototype(File); +const FilePrototype = File.prototype; + +webidl.converters["FilePropertyBag"] = webidl.createDictionaryConverter( + "FilePropertyBag", + blobPropertyBagDictionary, + [ + { + key: "lastModified", + converter: webidl.converters["long long"], + }, + ], +); + +// A finalization registry to deallocate a blob part when its JS reference is +// garbage collected. +const registry = new FinalizationRegistry((uuid) => { + ops.op_blob_remove_part(uuid); +}); + +// TODO(lucacasonato): get a better stream from Rust in BlobReference#stream + +/** + * An opaque reference to a blob part in Rust. This could be backed by a file, + * in memory storage, or something else. + */ +class BlobReference { /** - * Construct a new Blob object from an object URL. - * - * This new object will not duplicate data in memory with the original Blob - * object from which this URL was created or with other Blob objects created - * from the same URL, but they will be different objects. + * Don't use directly. Use `BlobReference.fromUint8Array`. + * @param {string} id + * @param {number} size + */ + constructor(id, size) { + this._id = id; + this.size = size; + registry.register(this, id); + } + + /** + * Create a new blob part from a Uint8Array. * - * The object returned from this function will not be a File object, even if - * the original object from which the object URL was constructed was one. This - * means that the `name` and `lastModified` properties are lost. + * @param {Uint8Array} data + * @returns {BlobReference} + */ + static fromUint8Array(data) { + const id = ops.op_blob_create_part(data); + return new BlobReference(id, data.byteLength); + } + + /** + * Create a new BlobReference by slicing this BlobReference. This is a copy + * free operation - the sliced reference will still reference the original + * underlying bytes. * - * @param {string} url - * @returns {Blob | null} + * @param {number} start + * @param {number} end + * @returns {BlobReference} */ - function blobFromObjectUrl(url) { - const blobData = ops.op_blob_from_object_url(url); - if (blobData === null) { - return null; - } + slice(start, end) { + const size = end - start; + const id = ops.op_blob_slice_part(this._id, { + start, + len: size, + }); + return new BlobReference(id, size); + } - /** @type {BlobReference[]} */ - const parts = []; - let totalSize = 0; + /** + * Read the entire contents of the reference blob. + * @returns {AsyncGenerator<Uint8Array>} + */ + async *stream() { + yield core.opAsync("op_blob_read_part", this._id); + + // let position = 0; + // const end = this.size; + // while (position !== end) { + // const size = MathMin(end - position, 65536); + // const chunk = this.slice(position, position + size); + // position += chunk.size; + // yield core.opAsync("op_blob_read_part", chunk._id); + // } + } +} + +/** + * Construct a new Blob object from an object URL. + * + * This new object will not duplicate data in memory with the original Blob + * object from which this URL was created or with other Blob objects created + * from the same URL, but they will be different objects. + * + * The object returned from this function will not be a File object, even if + * the original object from which the object URL was constructed was one. This + * means that the `name` and `lastModified` properties are lost. + * + * @param {string} url + * @returns {Blob | null} + */ +function blobFromObjectUrl(url) { + const blobData = ops.op_blob_from_object_url(url); + if (blobData === null) { + return null; + } - for (let i = 0; i < blobData.parts.length; ++i) { - const { uuid, size } = blobData.parts[i]; - ArrayPrototypePush(parts, new BlobReference(uuid, size)); - totalSize += size; - } + /** @type {BlobReference[]} */ + const parts = []; + let totalSize = 0; - const blob = webidl.createBranded(Blob); - blob[_type] = blobData.media_type; - blob[_size] = totalSize; - blob[_parts] = parts; - return blob; + for (let i = 0; i < blobData.parts.length; ++i) { + const { uuid, size } = blobData.parts[i]; + ArrayPrototypePush(parts, new BlobReference(uuid, size)); + totalSize += size; } - window.__bootstrap.file = { - blobFromObjectUrl, - getParts, - Blob, - BlobPrototype, - File, - FilePrototype, - }; -})(this); + const blob = webidl.createBranded(Blob); + blob[_type] = blobData.media_type; + blob[_size] = totalSize; + blob[_parts] = parts; + return blob; +} + +export { + Blob, + blobFromObjectUrl, + BlobPrototype, + File, + FilePrototype, + getParts, +}; diff --git a/ext/web/10_filereader.js b/ext/web/10_filereader.js index fb119f43e..7a46dfa9a 100644 --- a/ext/web/10_filereader.js +++ b/ext/web/10_filereader.js @@ -10,487 +10,482 @@ /// <reference path="./internal.d.ts" /> /// <reference lib="esnext" /> -"use strict"; - -((window) => { - const core = window.Deno.core; - const webidl = window.__bootstrap.webidl; - const { forgivingBase64Encode } = window.__bootstrap.infra; - const { ProgressEvent } = window.__bootstrap.event; - const { EventTarget } = window.__bootstrap.eventTarget; - const { decode, TextDecoder } = window.__bootstrap.encoding; - const { parseMimeType } = window.__bootstrap.mimesniff; - const { DOMException } = window.__bootstrap.domException; - const { - ArrayPrototypePush, - ArrayPrototypeReduce, - FunctionPrototypeCall, - Map, - MapPrototypeGet, - MapPrototypeSet, - ObjectDefineProperty, - ObjectPrototypeIsPrototypeOf, - queueMicrotask, - SafeArrayIterator, - Symbol, - TypedArrayPrototypeSet, - TypeError, - Uint8Array, - Uint8ArrayPrototype, - } = window.__bootstrap.primordials; - - const state = Symbol("[[state]]"); - const result = Symbol("[[result]]"); - const error = Symbol("[[error]]"); - const aborted = Symbol("[[aborted]]"); - const handlerSymbol = Symbol("eventHandlers"); - - class FileReader extends EventTarget { - /** @type {"empty" | "loading" | "done"} */ - [state] = "empty"; - /** @type {null | string | ArrayBuffer} */ - [result] = null; - /** @type {null | DOMException} */ - [error] = null; - /** @type {null | {aborted: boolean}} */ - [aborted] = null; - - /** - * @param {Blob} blob - * @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype - */ - #readOperation(blob, readtype) { - // 1. If fr’s state is "loading", throw an InvalidStateError DOMException. - if (this[state] === "loading") { - throw new DOMException( - "Invalid FileReader state.", - "InvalidStateError", - ); - } - // 2. Set fr’s state to "loading". - this[state] = "loading"; - // 3. Set fr’s result to null. - this[result] = null; - // 4. Set fr’s error to null. - this[error] = null; - - // We set this[aborted] to a new object, and keep track of it in a - // separate variable, so if a new read operation starts while there are - // remaining tasks from a previous aborted operation, the new operation - // will run while the tasks from the previous one are still aborted. - const abortedState = this[aborted] = { aborted: false }; - - // 5. Let stream be the result of calling get stream on blob. - const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream(); - - // 6. Let reader be the result of getting a reader from stream. - const reader = stream.getReader(); - - // 7. Let bytes be an empty byte sequence. - /** @type {Uint8Array[]} */ - const chunks = []; - - // 8. Let chunkPromise be the result of reading a chunk from stream with reader. - let chunkPromise = reader.read(); - - // 9. Let isFirstChunk be true. - let isFirstChunk = true; - - // 10 in parallel while true - (async () => { - while (!abortedState.aborted) { - // 1. Wait for chunkPromise to be fulfilled or rejected. - try { - const chunk = await chunkPromise; - if (abortedState.aborted) return; - - // 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr. - if (isFirstChunk) { +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +const primordials = globalThis.__bootstrap.primordials; +import { forgivingBase64Encode } from "internal:ext/web/00_infra.js"; +import { EventTarget, ProgressEvent } from "internal:ext/web/02_event.js"; +import { decode, TextDecoder } from "internal:ext/web/08_text_encoding.js"; +import { parseMimeType } from "internal:ext/web/01_mimesniff.js"; +import DOMException from "internal:ext/web/01_dom_exception.js"; +const { + ArrayPrototypePush, + ArrayPrototypeReduce, + FunctionPrototypeCall, + Map, + MapPrototypeGet, + MapPrototypeSet, + ObjectDefineProperty, + ObjectPrototypeIsPrototypeOf, + queueMicrotask, + SafeArrayIterator, + Symbol, + TypedArrayPrototypeSet, + TypeError, + Uint8Array, + Uint8ArrayPrototype, +} = primordials; + +const state = Symbol("[[state]]"); +const result = Symbol("[[result]]"); +const error = Symbol("[[error]]"); +const aborted = Symbol("[[aborted]]"); +const handlerSymbol = Symbol("eventHandlers"); + +class FileReader extends EventTarget { + /** @type {"empty" | "loading" | "done"} */ + [state] = "empty"; + /** @type {null | string | ArrayBuffer} */ + [result] = null; + /** @type {null | DOMException} */ + [error] = null; + /** @type {null | {aborted: boolean}} */ + [aborted] = null; + + /** + * @param {Blob} blob + * @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype + */ + #readOperation(blob, readtype) { + // 1. If fr’s state is "loading", throw an InvalidStateError DOMException. + if (this[state] === "loading") { + throw new DOMException( + "Invalid FileReader state.", + "InvalidStateError", + ); + } + // 2. Set fr’s state to "loading". + this[state] = "loading"; + // 3. Set fr’s result to null. + this[result] = null; + // 4. Set fr’s error to null. + this[error] = null; + + // We set this[aborted] to a new object, and keep track of it in a + // separate variable, so if a new read operation starts while there are + // remaining tasks from a previous aborted operation, the new operation + // will run while the tasks from the previous one are still aborted. + const abortedState = this[aborted] = { aborted: false }; + + // 5. Let stream be the result of calling get stream on blob. + const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream(); + + // 6. Let reader be the result of getting a reader from stream. + const reader = stream.getReader(); + + // 7. Let bytes be an empty byte sequence. + /** @type {Uint8Array[]} */ + const chunks = []; + + // 8. Let chunkPromise be the result of reading a chunk from stream with reader. + let chunkPromise = reader.read(); + + // 9. Let isFirstChunk be true. + let isFirstChunk = true; + + // 10 in parallel while true + (async () => { + while (!abortedState.aborted) { + // 1. Wait for chunkPromise to be fulfilled or rejected. + try { + const chunk = await chunkPromise; + if (abortedState.aborted) return; + + // 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr. + if (isFirstChunk) { + // TODO(lucacasonato): this is wrong, should be HTML "queue a task" + queueMicrotask(() => { + if (abortedState.aborted) return; + // fire a progress event for loadstart + const ev = new ProgressEvent("loadstart", {}); + this.dispatchEvent(ev); + }); + } + // 3. Set isFirstChunk to false. + isFirstChunk = false; + + // 4. If chunkPromise is fulfilled with an object whose done property is false + // and whose value property is a Uint8Array object, run these steps: + if ( + !chunk.done && + ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk.value) + ) { + ArrayPrototypePush(chunks, chunk.value); + + // TODO(bartlomieju): (only) If roughly 50ms have passed since last progress + { + const size = ArrayPrototypeReduce( + chunks, + (p, i) => p + i.byteLength, + 0, + ); + const ev = new ProgressEvent("progress", { + loaded: size, + }); // TODO(lucacasonato): this is wrong, should be HTML "queue a task" queueMicrotask(() => { if (abortedState.aborted) return; - // fire a progress event for loadstart - const ev = new ProgressEvent("loadstart", {}); this.dispatchEvent(ev); }); } - // 3. Set isFirstChunk to false. - isFirstChunk = false; - - // 4. If chunkPromise is fulfilled with an object whose done property is false - // and whose value property is a Uint8Array object, run these steps: - if ( - !chunk.done && - ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk.value) - ) { - ArrayPrototypePush(chunks, chunk.value); - - // TODO(bartlomieju): (only) If roughly 50ms have passed since last progress - { - const size = ArrayPrototypeReduce( - chunks, - (p, i) => p + i.byteLength, - 0, - ); - const ev = new ProgressEvent("progress", { - loaded: size, - }); - // TODO(lucacasonato): this is wrong, should be HTML "queue a task" - queueMicrotask(() => { - if (abortedState.aborted) return; - this.dispatchEvent(ev); - }); - } - chunkPromise = reader.read(); - } // 5 Otherwise, if chunkPromise is fulfilled with an object whose done property is true, queue a task to run the following steps and abort this algorithm: - else if (chunk.done === true) { - // TODO(lucacasonato): this is wrong, should be HTML "queue a task" - queueMicrotask(() => { - if (abortedState.aborted) return; - // 1. Set fr’s state to "done". - this[state] = "done"; - // 2. Let result be the result of package data given bytes, type, blob’s type, and encodingName. - const size = ArrayPrototypeReduce( - chunks, - (p, i) => p + i.byteLength, - 0, - ); - const bytes = new Uint8Array(size); - let offs = 0; - for (let i = 0; i < chunks.length; ++i) { - const chunk = chunks[i]; - TypedArrayPrototypeSet(bytes, chunk, offs); - offs += chunk.byteLength; + chunkPromise = reader.read(); + } // 5 Otherwise, if chunkPromise is fulfilled with an object whose done property is true, queue a task to run the following steps and abort this algorithm: + else if (chunk.done === true) { + // TODO(lucacasonato): this is wrong, should be HTML "queue a task" + queueMicrotask(() => { + if (abortedState.aborted) return; + // 1. Set fr’s state to "done". + this[state] = "done"; + // 2. Let result be the result of package data given bytes, type, blob’s type, and encodingName. + const size = ArrayPrototypeReduce( + chunks, + (p, i) => p + i.byteLength, + 0, + ); + const bytes = new Uint8Array(size); + let offs = 0; + for (let i = 0; i < chunks.length; ++i) { + const chunk = chunks[i]; + TypedArrayPrototypeSet(bytes, chunk, offs); + offs += chunk.byteLength; + } + switch (readtype.kind) { + case "ArrayBuffer": { + this[result] = bytes.buffer; + break; } - switch (readtype.kind) { - case "ArrayBuffer": { - this[result] = bytes.buffer; - break; - } - case "BinaryString": - this[result] = core.ops.op_encode_binary_string(bytes); - break; - case "Text": { - let decoder = undefined; - if (readtype.encoding) { - try { - decoder = new TextDecoder(readtype.encoding); - } catch { - // don't care about the error - } + case "BinaryString": + this[result] = ops.op_encode_binary_string(bytes); + break; + case "Text": { + let decoder = undefined; + if (readtype.encoding) { + try { + decoder = new TextDecoder(readtype.encoding); + } catch { + // don't care about the error } - if (decoder === undefined) { - const mimeType = parseMimeType(blob.type); - if (mimeType) { - const charset = MapPrototypeGet( - mimeType.parameters, - "charset", - ); - if (charset) { - try { - decoder = new TextDecoder(charset); - } catch { - // don't care about the error - } + } + if (decoder === undefined) { + const mimeType = parseMimeType(blob.type); + if (mimeType) { + const charset = MapPrototypeGet( + mimeType.parameters, + "charset", + ); + if (charset) { + try { + decoder = new TextDecoder(charset); + } catch { + // don't care about the error } } } - if (decoder === undefined) { - decoder = new TextDecoder(); - } - this[result] = decode(bytes, decoder.encoding); - break; } - case "DataUrl": { - const mediaType = blob.type || "application/octet-stream"; - this[result] = `data:${mediaType};base64,${ - forgivingBase64Encode(bytes) - }`; - break; + if (decoder === undefined) { + decoder = new TextDecoder(); } + this[result] = decode(bytes, decoder.encoding); + break; } - // 4.2 Fire a progress event called load at the fr. - { - const ev = new ProgressEvent("load", { - lengthComputable: true, - loaded: size, - total: size, - }); - this.dispatchEvent(ev); - } - - // 5. If fr’s state is not "loading", fire a progress event called loadend at the fr. - //Note: Event handler for the load or error events could have started another load, if that happens the loadend event for this load is not fired. - if (this[state] !== "loading") { - const ev = new ProgressEvent("loadend", { - lengthComputable: true, - loaded: size, - total: size, - }); - this.dispatchEvent(ev); + case "DataUrl": { + const mediaType = blob.type || "application/octet-stream"; + this[result] = `data:${mediaType};base64,${ + forgivingBase64Encode(bytes) + }`; + break; } - }); - break; - } - } catch (err) { - // TODO(lucacasonato): this is wrong, should be HTML "queue a task" - queueMicrotask(() => { - if (abortedState.aborted) return; - - // chunkPromise rejected - this[state] = "done"; - this[error] = err; - + } + // 4.2 Fire a progress event called load at the fr. { - const ev = new ProgressEvent("error", {}); + const ev = new ProgressEvent("load", { + lengthComputable: true, + loaded: size, + total: size, + }); this.dispatchEvent(ev); } - //If fr’s state is not "loading", fire a progress event called loadend at fr. - //Note: Event handler for the error event could have started another load, if that happens the loadend event for this load is not fired. + // 5. If fr’s state is not "loading", fire a progress event called loadend at the fr. + //Note: Event handler for the load or error events could have started another load, if that happens the loadend event for this load is not fired. if (this[state] !== "loading") { - const ev = new ProgressEvent("loadend", {}); + const ev = new ProgressEvent("loadend", { + lengthComputable: true, + loaded: size, + total: size, + }); this.dispatchEvent(ev); } }); break; } - } - })(); - } + } catch (err) { + // TODO(lucacasonato): this is wrong, should be HTML "queue a task" + queueMicrotask(() => { + if (abortedState.aborted) return; - #getEventHandlerFor(name) { - webidl.assertBranded(this, FileReaderPrototype); + // chunkPromise rejected + this[state] = "done"; + this[error] = err; - const maybeMap = this[handlerSymbol]; - if (!maybeMap) return null; + { + const ev = new ProgressEvent("error", {}); + this.dispatchEvent(ev); + } - return MapPrototypeGet(maybeMap, name)?.handler ?? null; - } + //If fr’s state is not "loading", fire a progress event called loadend at fr. + //Note: Event handler for the error event could have started another load, if that happens the loadend event for this load is not fired. + if (this[state] !== "loading") { + const ev = new ProgressEvent("loadend", {}); + this.dispatchEvent(ev); + } + }); + break; + } + } + })(); + } - #setEventHandlerFor(name, value) { - webidl.assertBranded(this, FileReaderPrototype); + #getEventHandlerFor(name) { + webidl.assertBranded(this, FileReaderPrototype); - if (!this[handlerSymbol]) { - this[handlerSymbol] = new Map(); - } - let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name); - if (handlerWrapper) { - handlerWrapper.handler = value; - } else { - handlerWrapper = makeWrappedHandler(value); - this.addEventListener(name, handlerWrapper); - } + const maybeMap = this[handlerSymbol]; + if (!maybeMap) return null; - MapPrototypeSet(this[handlerSymbol], name, handlerWrapper); - } + return MapPrototypeGet(maybeMap, name)?.handler ?? null; + } - constructor() { - super(); - this[webidl.brand] = webidl.brand; - } + #setEventHandlerFor(name, value) { + webidl.assertBranded(this, FileReaderPrototype); - /** @returns {number} */ - get readyState() { - webidl.assertBranded(this, FileReaderPrototype); - switch (this[state]) { - case "empty": - return FileReader.EMPTY; - case "loading": - return FileReader.LOADING; - case "done": - return FileReader.DONE; - default: - throw new TypeError("Invalid state"); - } + if (!this[handlerSymbol]) { + this[handlerSymbol] = new Map(); } - - get result() { - webidl.assertBranded(this, FileReaderPrototype); - return this[result]; + let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name); + if (handlerWrapper) { + handlerWrapper.handler = value; + } else { + handlerWrapper = makeWrappedHandler(value); + this.addEventListener(name, handlerWrapper); } - get error() { - webidl.assertBranded(this, FileReaderPrototype); - return this[error]; + MapPrototypeSet(this[handlerSymbol], name, handlerWrapper); + } + + constructor() { + super(); + this[webidl.brand] = webidl.brand; + } + + /** @returns {number} */ + get readyState() { + webidl.assertBranded(this, FileReaderPrototype); + switch (this[state]) { + case "empty": + return FileReader.EMPTY; + case "loading": + return FileReader.LOADING; + case "done": + return FileReader.DONE; + default: + throw new TypeError("Invalid state"); } + } - abort() { - webidl.assertBranded(this, FileReaderPrototype); - // If context object's state is "empty" or if context object's state is "done" set context object's result to null and terminate this algorithm. - if ( - this[state] === "empty" || - this[state] === "done" - ) { - this[result] = null; - return; - } - // If context object's state is "loading" set context object's state to "done" and set context object's result to null. - if (this[state] === "loading") { - this[state] = "done"; - this[result] = null; - } - // If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue. - // Terminate the algorithm for the read method being processed. - if (this[aborted] !== null) { - this[aborted].aborted = true; - } + get result() { + webidl.assertBranded(this, FileReaderPrototype); + return this[result]; + } - // Fire a progress event called abort at the context object. - const ev = new ProgressEvent("abort", {}); - this.dispatchEvent(ev); + get error() { + webidl.assertBranded(this, FileReaderPrototype); + return this[error]; + } - // If context object's state is not "loading", fire a progress event called loadend at the context object. - if (this[state] !== "loading") { - const ev = new ProgressEvent("loadend", {}); - this.dispatchEvent(ev); - } + abort() { + webidl.assertBranded(this, FileReaderPrototype); + // If context object's state is "empty" or if context object's state is "done" set context object's result to null and terminate this algorithm. + if ( + this[state] === "empty" || + this[state] === "done" + ) { + this[result] = null; + return; } - - /** @param {Blob} blob */ - readAsArrayBuffer(blob) { - webidl.assertBranded(this, FileReaderPrototype); - const prefix = "Failed to execute 'readAsArrayBuffer' on 'FileReader'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - this.#readOperation(blob, { kind: "ArrayBuffer" }); + // If context object's state is "loading" set context object's state to "done" and set context object's result to null. + if (this[state] === "loading") { + this[state] = "done"; + this[result] = null; } - - /** @param {Blob} blob */ - readAsBinaryString(blob) { - webidl.assertBranded(this, FileReaderPrototype); - const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - // alias for readAsArrayBuffer - this.#readOperation(blob, { kind: "BinaryString" }); + // If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue. + // Terminate the algorithm for the read method being processed. + if (this[aborted] !== null) { + this[aborted].aborted = true; } - /** @param {Blob} blob */ - readAsDataURL(blob) { - webidl.assertBranded(this, FileReaderPrototype); - const prefix = "Failed to execute 'readAsDataURL' on 'FileReader'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - // alias for readAsArrayBuffer - this.#readOperation(blob, { kind: "DataUrl" }); - } + // Fire a progress event called abort at the context object. + const ev = new ProgressEvent("abort", {}); + this.dispatchEvent(ev); - /** - * @param {Blob} blob - * @param {string} [encoding] - */ - readAsText(blob, encoding = undefined) { - webidl.assertBranded(this, FileReaderPrototype); - const prefix = "Failed to execute 'readAsText' on 'FileReader'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - if (encoding !== undefined) { - encoding = webidl.converters["DOMString"](encoding, { - prefix, - context: "Argument 2", - }); - } - // alias for readAsArrayBuffer - this.#readOperation(blob, { kind: "Text", encoding }); + // If context object's state is not "loading", fire a progress event called loadend at the context object. + if (this[state] !== "loading") { + const ev = new ProgressEvent("loadend", {}); + this.dispatchEvent(ev); } + } - get onerror() { - return this.#getEventHandlerFor("error"); - } - set onerror(value) { - this.#setEventHandlerFor("error", value); - } + /** @param {Blob} blob */ + readAsArrayBuffer(blob) { + webidl.assertBranded(this, FileReaderPrototype); + const prefix = "Failed to execute 'readAsArrayBuffer' on 'FileReader'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + this.#readOperation(blob, { kind: "ArrayBuffer" }); + } - get onloadstart() { - return this.#getEventHandlerFor("loadstart"); - } - set onloadstart(value) { - this.#setEventHandlerFor("loadstart", value); - } + /** @param {Blob} blob */ + readAsBinaryString(blob) { + webidl.assertBranded(this, FileReaderPrototype); + const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + // alias for readAsArrayBuffer + this.#readOperation(blob, { kind: "BinaryString" }); + } - get onload() { - return this.#getEventHandlerFor("load"); - } - set onload(value) { - this.#setEventHandlerFor("load", value); - } + /** @param {Blob} blob */ + readAsDataURL(blob) { + webidl.assertBranded(this, FileReaderPrototype); + const prefix = "Failed to execute 'readAsDataURL' on 'FileReader'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + // alias for readAsArrayBuffer + this.#readOperation(blob, { kind: "DataUrl" }); + } - get onloadend() { - return this.#getEventHandlerFor("loadend"); - } - set onloadend(value) { - this.#setEventHandlerFor("loadend", value); + /** + * @param {Blob} blob + * @param {string} [encoding] + */ + readAsText(blob, encoding = undefined) { + webidl.assertBranded(this, FileReaderPrototype); + const prefix = "Failed to execute 'readAsText' on 'FileReader'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + if (encoding !== undefined) { + encoding = webidl.converters["DOMString"](encoding, { + prefix, + context: "Argument 2", + }); } + // alias for readAsArrayBuffer + this.#readOperation(blob, { kind: "Text", encoding }); + } - get onprogress() { - return this.#getEventHandlerFor("progress"); - } - set onprogress(value) { - this.#setEventHandlerFor("progress", value); - } + get onerror() { + return this.#getEventHandlerFor("error"); + } + set onerror(value) { + this.#setEventHandlerFor("error", value); + } - get onabort() { - return this.#getEventHandlerFor("abort"); - } - set onabort(value) { - this.#setEventHandlerFor("abort", value); - } + get onloadstart() { + return this.#getEventHandlerFor("loadstart"); + } + set onloadstart(value) { + this.#setEventHandlerFor("loadstart", value); } - webidl.configurePrototype(FileReader); - const FileReaderPrototype = FileReader.prototype; - - ObjectDefineProperty(FileReader, "EMPTY", { - writable: false, - enumerable: true, - configurable: false, - value: 0, - }); - ObjectDefineProperty(FileReader, "LOADING", { - writable: false, - enumerable: true, - configurable: false, - value: 1, - }); - ObjectDefineProperty(FileReader, "DONE", { - writable: false, - enumerable: true, - configurable: false, - value: 2, - }); - ObjectDefineProperty(FileReader.prototype, "EMPTY", { - writable: false, - enumerable: true, - configurable: false, - value: 0, - }); - ObjectDefineProperty(FileReader.prototype, "LOADING", { - writable: false, - enumerable: true, - configurable: false, - value: 1, - }); - ObjectDefineProperty(FileReader.prototype, "DONE", { - writable: false, - enumerable: true, - configurable: false, - value: 2, - }); - - function makeWrappedHandler(handler) { - function wrappedHandler(...args) { - if (typeof wrappedHandler.handler !== "function") { - return; - } - return FunctionPrototypeCall( - wrappedHandler.handler, - this, - ...new SafeArrayIterator(args), - ); + get onload() { + return this.#getEventHandlerFor("load"); + } + set onload(value) { + this.#setEventHandlerFor("load", value); + } + + get onloadend() { + return this.#getEventHandlerFor("loadend"); + } + set onloadend(value) { + this.#setEventHandlerFor("loadend", value); + } + + get onprogress() { + return this.#getEventHandlerFor("progress"); + } + set onprogress(value) { + this.#setEventHandlerFor("progress", value); + } + + get onabort() { + return this.#getEventHandlerFor("abort"); + } + set onabort(value) { + this.#setEventHandlerFor("abort", value); + } +} + +webidl.configurePrototype(FileReader); +const FileReaderPrototype = FileReader.prototype; + +ObjectDefineProperty(FileReader, "EMPTY", { + writable: false, + enumerable: true, + configurable: false, + value: 0, +}); +ObjectDefineProperty(FileReader, "LOADING", { + writable: false, + enumerable: true, + configurable: false, + value: 1, +}); +ObjectDefineProperty(FileReader, "DONE", { + writable: false, + enumerable: true, + configurable: false, + value: 2, +}); +ObjectDefineProperty(FileReader.prototype, "EMPTY", { + writable: false, + enumerable: true, + configurable: false, + value: 0, +}); +ObjectDefineProperty(FileReader.prototype, "LOADING", { + writable: false, + enumerable: true, + configurable: false, + value: 1, +}); +ObjectDefineProperty(FileReader.prototype, "DONE", { + writable: false, + enumerable: true, + configurable: false, + value: 2, +}); + +function makeWrappedHandler(handler) { + function wrappedHandler(...args) { + if (typeof wrappedHandler.handler !== "function") { + return; } - wrappedHandler.handler = handler; - return wrappedHandler; + return FunctionPrototypeCall( + wrappedHandler.handler, + this, + ...new SafeArrayIterator(args), + ); } + wrappedHandler.handler = handler; + return wrappedHandler; +} - window.__bootstrap.fileReader = { - FileReader, - }; -})(this); +export { FileReader }; diff --git a/ext/web/11_blob_url.js b/ext/web/11_blob_url.js index a51a1e718..02551fef6 100644 --- a/ext/web/11_blob_url.js +++ b/ext/web/11_blob_url.js @@ -10,50 +10,42 @@ /// <reference path="../url/lib.deno_url.d.ts" /> /// <reference path="./internal.d.ts" /> /// <reference lib="esnext" /> -"use strict"; -((window) => { - const core = Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { getParts } = window.__bootstrap.file; - const { URL } = window.__bootstrap.url; - - /** - * @param {Blob} blob - * @returns {string} - */ - function createObjectURL(blob) { - const prefix = "Failed to execute 'createObjectURL' on 'URL'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - blob = webidl.converters["Blob"](blob, { - context: "Argument 1", - prefix, - }); - - const url = ops.op_blob_create_object_url( - blob.type, - getParts(blob), - ); - - return url; - } - - /** - * @param {string} url - * @returns {void} - */ - function revokeObjectURL(url) { - const prefix = "Failed to execute 'revokeObjectURL' on 'URL'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - url = webidl.converters["DOMString"](url, { - context: "Argument 1", - prefix, - }); - - ops.op_blob_revoke_object_url(url); - } - - URL.createObjectURL = createObjectURL; - URL.revokeObjectURL = revokeObjectURL; -})(globalThis); +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { getParts } from "internal:ext/web/09_file.js"; +import { URL } from "internal:ext/url/00_url.js"; + +/** + * @param {Blob} blob + * @returns {string} + */ +function createObjectURL(blob) { + const prefix = "Failed to execute 'createObjectURL' on 'URL'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + blob = webidl.converters["Blob"](blob, { + context: "Argument 1", + prefix, + }); + + return ops.op_blob_create_object_url(blob.type, getParts(blob)); +} + +/** + * @param {string} url + * @returns {void} + */ +function revokeObjectURL(url) { + const prefix = "Failed to execute 'revokeObjectURL' on 'URL'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + url = webidl.converters["DOMString"](url, { + context: "Argument 1", + prefix, + }); + + ops.op_blob_revoke_object_url(url); +} + +URL.createObjectURL = createObjectURL; +URL.revokeObjectURL = revokeObjectURL; diff --git a/ext/web/12_location.js b/ext/web/12_location.js index 964ca591e..da964eae8 100644 --- a/ext/web/12_location.js +++ b/ext/web/12_location.js @@ -1,403 +1,410 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; /// <reference path="../../core/internal.d.ts" /> -((window) => { - const { URL } = window.__bootstrap.url; - const { DOMException } = window.__bootstrap.domException; - const { - Error, - ObjectDefineProperties, - Symbol, - SymbolFor, - SymbolToStringTag, - TypeError, - WeakMap, - WeakMapPrototypeGet, - WeakMapPrototypeSet, - } = window.__bootstrap.primordials; +import { URL } from "internal:ext/url/00_url.js"; +import DOMException from "internal:ext/web/01_dom_exception.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + Error, + ObjectDefineProperties, + Symbol, + SymbolFor, + SymbolToStringTag, + TypeError, + WeakMap, + WeakMapPrototypeGet, + WeakMapPrototypeSet, +} = primordials; - const locationConstructorKey = Symbol("locationConstuctorKey"); +const locationConstructorKey = Symbol("locationConstuctorKey"); - // The differences between the definitions of `Location` and `WorkerLocation` - // are because of the `LegacyUnforgeable` attribute only specified upon - // `Location`'s properties. See: - // - https://html.spec.whatwg.org/multipage/history.html#the-location-interface - // - https://heycam.github.io/webidl/#LegacyUnforgeable - class Location { - constructor(href = null, key = null) { - if (key != locationConstructorKey) { - throw new TypeError("Illegal constructor."); - } - const url = new URL(href); - url.username = ""; - url.password = ""; - ObjectDefineProperties(this, { - hash: { - get() { - return url.hash; - }, - set() { - throw new DOMException( - `Cannot set "location.hash".`, - "NotSupportedError", - ); - }, - enumerable: true, +// The differences between the definitions of `Location` and `WorkerLocation` +// are because of the `LegacyUnforgeable` attribute only specified upon +// `Location`'s properties. See: +// - https://html.spec.whatwg.org/multipage/history.html#the-location-interface +// - https://heycam.github.io/webidl/#LegacyUnforgeable +class Location { + constructor(href = null, key = null) { + if (key != locationConstructorKey) { + throw new TypeError("Illegal constructor."); + } + const url = new URL(href); + url.username = ""; + url.password = ""; + ObjectDefineProperties(this, { + hash: { + get() { + return url.hash; }, - host: { - get() { - return url.host; - }, - set() { - throw new DOMException( - `Cannot set "location.host".`, - "NotSupportedError", - ); - }, - enumerable: true, + set() { + throw new DOMException( + `Cannot set "location.hash".`, + "NotSupportedError", + ); }, - hostname: { - get() { - return url.hostname; - }, - set() { - throw new DOMException( - `Cannot set "location.hostname".`, - "NotSupportedError", - ); - }, - enumerable: true, + enumerable: true, + }, + host: { + get() { + return url.host; }, - href: { - get() { - return url.href; - }, - set() { - throw new DOMException( - `Cannot set "location.href".`, - "NotSupportedError", - ); - }, - enumerable: true, + set() { + throw new DOMException( + `Cannot set "location.host".`, + "NotSupportedError", + ); }, - origin: { - get() { - return url.origin; - }, - enumerable: true, + enumerable: true, + }, + hostname: { + get() { + return url.hostname; }, - pathname: { - get() { - return url.pathname; - }, - set() { - throw new DOMException( - `Cannot set "location.pathname".`, - "NotSupportedError", - ); - }, - enumerable: true, + set() { + throw new DOMException( + `Cannot set "location.hostname".`, + "NotSupportedError", + ); }, - port: { - get() { - return url.port; - }, - set() { - throw new DOMException( - `Cannot set "location.port".`, - "NotSupportedError", - ); - }, - enumerable: true, + enumerable: true, + }, + href: { + get() { + return url.href; }, - protocol: { - get() { - return url.protocol; - }, - set() { - throw new DOMException( - `Cannot set "location.protocol".`, - "NotSupportedError", - ); - }, - enumerable: true, + set() { + throw new DOMException( + `Cannot set "location.href".`, + "NotSupportedError", + ); }, - search: { - get() { - return url.search; - }, - set() { - throw new DOMException( - `Cannot set "location.search".`, - "NotSupportedError", - ); - }, - enumerable: true, + enumerable: true, + }, + origin: { + get() { + return url.origin; }, - ancestorOrigins: { - get() { - // TODO(nayeemrmn): Replace with a `DOMStringList` instance. - return { - length: 0, - item: () => null, - contains: () => false, - }; - }, - enumerable: true, + enumerable: true, + }, + pathname: { + get() { + return url.pathname; }, - assign: { - value: function assign() { - throw new DOMException( - `Cannot call "location.assign()".`, - "NotSupportedError", - ); - }, - enumerable: true, + set() { + throw new DOMException( + `Cannot set "location.pathname".`, + "NotSupportedError", + ); }, - reload: { - value: function reload() { - throw new DOMException( - `Cannot call "location.reload()".`, - "NotSupportedError", - ); - }, - enumerable: true, + enumerable: true, + }, + port: { + get() { + return url.port; }, - replace: { - value: function replace() { - throw new DOMException( - `Cannot call "location.replace()".`, - "NotSupportedError", - ); - }, - enumerable: true, + set() { + throw new DOMException( + `Cannot set "location.port".`, + "NotSupportedError", + ); }, - toString: { - value: function toString() { - return url.href; - }, - enumerable: true, + enumerable: true, + }, + protocol: { + get() { + return url.protocol; }, - [SymbolFor("Deno.privateCustomInspect")]: { - value: function (inspect) { - const object = { - hash: this.hash, - host: this.host, - hostname: this.hostname, - href: this.href, - origin: this.origin, - pathname: this.pathname, - port: this.port, - protocol: this.protocol, - search: this.search, - }; - return `${this.constructor.name} ${inspect(object)}`; - }, + set() { + throw new DOMException( + `Cannot set "location.protocol".`, + "NotSupportedError", + ); }, - }); - } + enumerable: true, + }, + search: { + get() { + return url.search; + }, + set() { + throw new DOMException( + `Cannot set "location.search".`, + "NotSupportedError", + ); + }, + enumerable: true, + }, + ancestorOrigins: { + get() { + // TODO(nayeemrmn): Replace with a `DOMStringList` instance. + return { + length: 0, + item: () => null, + contains: () => false, + }; + }, + enumerable: true, + }, + assign: { + value: function assign() { + throw new DOMException( + `Cannot call "location.assign()".`, + "NotSupportedError", + ); + }, + enumerable: true, + }, + reload: { + value: function reload() { + throw new DOMException( + `Cannot call "location.reload()".`, + "NotSupportedError", + ); + }, + enumerable: true, + }, + replace: { + value: function replace() { + throw new DOMException( + `Cannot call "location.replace()".`, + "NotSupportedError", + ); + }, + enumerable: true, + }, + toString: { + value: function toString() { + return url.href; + }, + enumerable: true, + }, + [SymbolFor("Deno.privateCustomInspect")]: { + value: function (inspect) { + const object = { + hash: this.hash, + host: this.host, + hostname: this.hostname, + href: this.href, + origin: this.origin, + pathname: this.pathname, + port: this.port, + protocol: this.protocol, + search: this.search, + }; + return `${this.constructor.name} ${inspect(object)}`; + }, + }, + }); } +} - ObjectDefineProperties(Location.prototype, { - [SymbolToStringTag]: { - value: "Location", - configurable: true, - }, - }); +ObjectDefineProperties(Location.prototype, { + [SymbolToStringTag]: { + value: "Location", + configurable: true, + }, +}); - const workerLocationUrls = new WeakMap(); +const workerLocationUrls = new WeakMap(); - class WorkerLocation { - constructor(href = null, key = null) { - if (key != locationConstructorKey) { - throw new TypeError("Illegal constructor."); - } - const url = new URL(href); - url.username = ""; - url.password = ""; - WeakMapPrototypeSet(workerLocationUrls, this, url); +class WorkerLocation { + constructor(href = null, key = null) { + if (key != locationConstructorKey) { + throw new TypeError("Illegal constructor."); } + const url = new URL(href); + url.username = ""; + url.password = ""; + WeakMapPrototypeSet(workerLocationUrls, this, url); } +} - ObjectDefineProperties(WorkerLocation.prototype, { - hash: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.hash; - }, - configurable: true, - enumerable: true, - }, - host: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.host; - }, - configurable: true, - enumerable: true, +ObjectDefineProperties(WorkerLocation.prototype, { + hash: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.hash; }, - hostname: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.hostname; - }, - configurable: true, - enumerable: true, + configurable: true, + enumerable: true, + }, + host: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.host; }, - href: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.href; - }, - configurable: true, - enumerable: true, + configurable: true, + enumerable: true, + }, + hostname: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.hostname; }, - origin: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.origin; - }, - configurable: true, - enumerable: true, + configurable: true, + enumerable: true, + }, + href: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.href; }, - pathname: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.pathname; - }, - configurable: true, - enumerable: true, + configurable: true, + enumerable: true, + }, + origin: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.origin; }, - port: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.port; - }, - configurable: true, - enumerable: true, + configurable: true, + enumerable: true, + }, + pathname: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.pathname; }, - protocol: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.protocol; - }, - configurable: true, - enumerable: true, + configurable: true, + enumerable: true, + }, + port: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.port; }, - search: { - get() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.search; - }, - configurable: true, - enumerable: true, + configurable: true, + enumerable: true, + }, + protocol: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.protocol; }, - toString: { - value: function toString() { - const url = WeakMapPrototypeGet(workerLocationUrls, this); - if (url == null) { - throw new TypeError("Illegal invocation."); - } - return url.href; - }, - configurable: true, - enumerable: true, - writable: true, + configurable: true, + enumerable: true, + }, + search: { + get() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.search; }, - [SymbolToStringTag]: { - value: "WorkerLocation", - configurable: true, + configurable: true, + enumerable: true, + }, + toString: { + value: function toString() { + const url = WeakMapPrototypeGet(workerLocationUrls, this); + if (url == null) { + throw new TypeError("Illegal invocation."); + } + return url.href; }, - [SymbolFor("Deno.privateCustomInspect")]: { - value: function (inspect) { - const object = { - hash: this.hash, - host: this.host, - hostname: this.hostname, - href: this.href, - origin: this.origin, - pathname: this.pathname, - port: this.port, - protocol: this.protocol, - search: this.search, - }; - return `${this.constructor.name} ${inspect(object)}`; - }, + configurable: true, + enumerable: true, + writable: true, + }, + [SymbolToStringTag]: { + value: "WorkerLocation", + configurable: true, + }, + [SymbolFor("Deno.privateCustomInspect")]: { + value: function (inspect) { + const object = { + hash: this.hash, + host: this.host, + hostname: this.hostname, + href: this.href, + origin: this.origin, + pathname: this.pathname, + port: this.port, + protocol: this.protocol, + search: this.search, + }; + return `${this.constructor.name} ${inspect(object)}`; }, - }); + }, +}); - let location = undefined; - let workerLocation = undefined; +let location = undefined; +let workerLocation = undefined; - function setLocationHref(href) { - location = new Location(href, locationConstructorKey); - workerLocation = new WorkerLocation(href, locationConstructorKey); - } +function setLocationHref(href) { + location = new Location(href, locationConstructorKey); + workerLocation = new WorkerLocation(href, locationConstructorKey); +} - window.__bootstrap.location = { - locationConstructorDescriptor: { - value: Location, - configurable: true, - writable: true, - }, - workerLocationConstructorDescriptor: { - value: WorkerLocation, - configurable: true, - writable: true, - }, - locationDescriptor: { - get() { - return location; - }, - set() { - throw new DOMException(`Cannot set "location".`, "NotSupportedError"); - }, - enumerable: true, - }, - workerLocationDescriptor: { - get() { - if (workerLocation == null) { - throw new Error( - `Assertion: "globalThis.location" must be defined in a worker.`, - ); - } - return workerLocation; - }, - configurable: true, - enumerable: true, - }, - setLocationHref, - getLocationHref() { - return location?.href; - }, - }; -})(this); +function getLocationHref() { + return location?.href; +} + +const locationConstructorDescriptor = { + value: Location, + configurable: true, + writable: true, +}; + +const workerLocationConstructorDescriptor = { + value: WorkerLocation, + configurable: true, + writable: true, +}; + +const locationDescriptor = { + get() { + return location; + }, + set() { + throw new DOMException(`Cannot set "location".`, "NotSupportedError"); + }, + enumerable: true, +}; +const workerLocationDescriptor = { + get() { + if (workerLocation == null) { + throw new Error( + `Assertion: "globalThis.location" must be defined in a worker.`, + ); + } + return workerLocation; + }, + configurable: true, + enumerable: true, +}; + +export { + getLocationHref, + locationConstructorDescriptor, + locationDescriptor, + setLocationHref, + workerLocationConstructorDescriptor, + workerLocationDescriptor, +}; diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index 7ab2beb82..2a784bf3f 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -6,338 +6,339 @@ /// <reference path="./internal.d.ts" /> /// <reference path="./lib.deno_web.d.ts" /> -"use strict"; - -((window) => { - const core = window.Deno.core; - const { InterruptedPrototype, ops } = core; - const webidl = window.__bootstrap.webidl; - const { EventTarget, setEventTargetData } = window.__bootstrap.eventTarget; - const { MessageEvent, defineEventHandler } = window.__bootstrap.event; - const { DOMException } = window.__bootstrap.domException; - const { - ArrayBufferPrototype, - ArrayPrototypeFilter, - ArrayPrototypeIncludes, - ArrayPrototypePush, - ObjectPrototypeIsPrototypeOf, - ObjectSetPrototypeOf, - Symbol, - SymbolFor, - SymbolIterator, - TypeError, - } = window.__bootstrap.primordials; - - class MessageChannel { - /** @type {MessagePort} */ - #port1; - /** @type {MessagePort} */ - #port2; - - constructor() { - this[webidl.brand] = webidl.brand; - const { 0: port1Id, 1: port2Id } = opCreateEntangledMessagePort(); - const port1 = createMessagePort(port1Id); - const port2 = createMessagePort(port2Id); - this.#port1 = port1; - this.#port2 = port2; - } - - get port1() { - webidl.assertBranded(this, MessageChannelPrototype); - return this.#port1; - } - - get port2() { - webidl.assertBranded(this, MessageChannelPrototype); - return this.#port2; - } +const core = globalThis.Deno.core; +const { InterruptedPrototype, ops } = core; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { + defineEventHandler, + EventTarget, + MessageEvent, + setEventTargetData, +} from "internal:ext/web/02_event.js"; +import DOMException from "internal:ext/web/01_dom_exception.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayBufferPrototype, + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ArrayPrototypePush, + ObjectPrototypeIsPrototypeOf, + ObjectSetPrototypeOf, + Symbol, + SymbolFor, + SymbolIterator, + TypeError, +} = primordials; + +class MessageChannel { + /** @type {MessagePort} */ + #port1; + /** @type {MessagePort} */ + #port2; + + constructor() { + this[webidl.brand] = webidl.brand; + const { 0: port1Id, 1: port2Id } = opCreateEntangledMessagePort(); + const port1 = createMessagePort(port1Id); + const port2 = createMessagePort(port2Id); + this.#port1 = port1; + this.#port2 = port2; + } - [SymbolFor("Deno.inspect")](inspect) { - return `MessageChannel ${ - inspect({ port1: this.port1, port2: this.port2 }) - }`; - } + get port1() { + webidl.assertBranded(this, MessageChannelPrototype); + return this.#port1; } - webidl.configurePrototype(MessageChannel); - const MessageChannelPrototype = MessageChannel.prototype; + get port2() { + webidl.assertBranded(this, MessageChannelPrototype); + return this.#port2; + } - const _id = Symbol("id"); - const _enabled = Symbol("enabled"); + [SymbolFor("Deno.inspect")](inspect) { + return `MessageChannel ${ + inspect({ port1: this.port1, port2: this.port2 }) + }`; + } +} + +webidl.configurePrototype(MessageChannel); +const MessageChannelPrototype = MessageChannel.prototype; + +const _id = Symbol("id"); +const _enabled = Symbol("enabled"); + +/** + * @param {number} id + * @returns {MessagePort} + */ +function createMessagePort(id) { + const port = core.createHostObject(); + ObjectSetPrototypeOf(port, MessagePortPrototype); + port[webidl.brand] = webidl.brand; + setEventTargetData(port); + port[_id] = id; + return port; +} + +class MessagePort extends EventTarget { + /** @type {number | null} */ + [_id] = null; + /** @type {boolean} */ + [_enabled] = false; + + constructor() { + super(); + webidl.illegalConstructor(); + } /** - * @param {number} id - * @returns {MessagePort} + * @param {any} message + * @param {object[] | StructuredSerializeOptions} transferOrOptions */ - function createMessagePort(id) { - const port = core.createHostObject(); - ObjectSetPrototypeOf(port, MessagePortPrototype); - port[webidl.brand] = webidl.brand; - setEventTargetData(port); - port[_id] = id; - return port; - } - - class MessagePort extends EventTarget { - /** @type {number | null} */ - [_id] = null; - /** @type {boolean} */ - [_enabled] = false; - - constructor() { - super(); - webidl.illegalConstructor(); + postMessage(message, transferOrOptions = {}) { + webidl.assertBranded(this, MessagePortPrototype); + const prefix = "Failed to execute 'postMessage' on 'MessagePort'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + message = webidl.converters.any(message); + let options; + if ( + webidl.type(transferOrOptions) === "Object" && + transferOrOptions !== undefined && + transferOrOptions[SymbolIterator] !== undefined + ) { + const transfer = webidl.converters["sequence<object>"]( + transferOrOptions, + { prefix, context: "Argument 2" }, + ); + options = { transfer }; + } else { + options = webidl.converters.StructuredSerializeOptions( + transferOrOptions, + { + prefix, + context: "Argument 2", + }, + ); } - - /** - * @param {any} message - * @param {object[] | StructuredSerializeOptions} transferOrOptions - */ - postMessage(message, transferOrOptions = {}) { - webidl.assertBranded(this, MessagePortPrototype); - const prefix = "Failed to execute 'postMessage' on 'MessagePort'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - message = webidl.converters.any(message); - let options; - if ( - webidl.type(transferOrOptions) === "Object" && - transferOrOptions !== undefined && - transferOrOptions[SymbolIterator] !== undefined - ) { - const transfer = webidl.converters["sequence<object>"]( - transferOrOptions, - { prefix, context: "Argument 2" }, - ); - options = { transfer }; - } else { - options = webidl.converters.StructuredSerializeOptions( - transferOrOptions, - { - prefix, - context: "Argument 2", - }, - ); - } - const { transfer } = options; - if (ArrayPrototypeIncludes(transfer, this)) { - throw new DOMException("Can not tranfer self", "DataCloneError"); - } - const data = serializeJsMessageData(message, transfer); - if (this[_id] === null) return; - ops.op_message_port_post_message(this[_id], data); + const { transfer } = options; + if (ArrayPrototypeIncludes(transfer, this)) { + throw new DOMException("Can not tranfer self", "DataCloneError"); } + const data = serializeJsMessageData(message, transfer); + if (this[_id] === null) return; + ops.op_message_port_post_message(this[_id], data); + } - start() { - webidl.assertBranded(this, MessagePortPrototype); - if (this[_enabled]) return; - (async () => { - this[_enabled] = true; - while (true) { - if (this[_id] === null) break; - let data; - try { - data = await core.opAsync( - "op_message_port_recv_message", - this[_id], - ); - } catch (err) { - if (ObjectPrototypeIsPrototypeOf(InterruptedPrototype, err)) break; - throw err; - } - if (data === null) break; - let message, transferables; - try { - const v = deserializeJsMessageData(data); - message = v[0]; - transferables = v[1]; - } catch (err) { - const event = new MessageEvent("messageerror", { data: err }); - this.dispatchEvent(event); - return; - } - const event = new MessageEvent("message", { - data: message, - ports: ArrayPrototypeFilter( - transferables, - (t) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t), - ), - }); + start() { + webidl.assertBranded(this, MessagePortPrototype); + if (this[_enabled]) return; + (async () => { + this[_enabled] = true; + while (true) { + if (this[_id] === null) break; + let data; + try { + data = await core.opAsync( + "op_message_port_recv_message", + this[_id], + ); + } catch (err) { + if (ObjectPrototypeIsPrototypeOf(InterruptedPrototype, err)) break; + throw err; + } + if (data === null) break; + let message, transferables; + try { + const v = deserializeJsMessageData(data); + message = v[0]; + transferables = v[1]; + } catch (err) { + const event = new MessageEvent("messageerror", { data: err }); this.dispatchEvent(event); + return; } - this[_enabled] = false; - })(); - } - - close() { - webidl.assertBranded(this, MessagePortPrototype); - if (this[_id] !== null) { - core.close(this[_id]); - this[_id] = null; + const event = new MessageEvent("message", { + data: message, + ports: ArrayPrototypeFilter( + transferables, + (t) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t), + ), + }); + this.dispatchEvent(event); } - } + this[_enabled] = false; + })(); } - defineEventHandler(MessagePort.prototype, "message", function (self) { - self.start(); - }); - defineEventHandler(MessagePort.prototype, "messageerror"); - - webidl.configurePrototype(MessagePort); - const MessagePortPrototype = MessagePort.prototype; - - /** - * @returns {[number, number]} - */ - function opCreateEntangledMessagePort() { - return ops.op_message_port_create_entangled(); + close() { + webidl.assertBranded(this, MessagePortPrototype); + if (this[_id] !== null) { + core.close(this[_id]); + this[_id] = null; + } } - - /** - * @param {globalThis.__bootstrap.messagePort.MessageData} messageData - * @returns {[any, object[]]} - */ - function deserializeJsMessageData(messageData) { - /** @type {object[]} */ - const transferables = []; - const hostObjects = []; - const arrayBufferIdsInTransferables = []; - const transferredArrayBuffers = []; - - for (let i = 0; i < messageData.transferables.length; ++i) { - const transferable = messageData.transferables[i]; - switch (transferable.kind) { - case "messagePort": { - const port = createMessagePort(transferable.data); - ArrayPrototypePush(transferables, port); - ArrayPrototypePush(hostObjects, port); - break; - } - case "arrayBuffer": { - ArrayPrototypePush(transferredArrayBuffers, transferable.data); - const index = ArrayPrototypePush(transferables, null); - ArrayPrototypePush(arrayBufferIdsInTransferables, index); - break; - } - default: - throw new TypeError("Unreachable"); +} + +defineEventHandler(MessagePort.prototype, "message", function (self) { + self.start(); +}); +defineEventHandler(MessagePort.prototype, "messageerror"); + +webidl.configurePrototype(MessagePort); +const MessagePortPrototype = MessagePort.prototype; + +/** + * @returns {[number, number]} + */ +function opCreateEntangledMessagePort() { + return ops.op_message_port_create_entangled(); +} + +/** + * @param {messagePort.MessageData} messageData + * @returns {[any, object[]]} + */ +function deserializeJsMessageData(messageData) { + /** @type {object[]} */ + const transferables = []; + const hostObjects = []; + const arrayBufferIdsInTransferables = []; + const transferredArrayBuffers = []; + + for (let i = 0; i < messageData.transferables.length; ++i) { + const transferable = messageData.transferables[i]; + switch (transferable.kind) { + case "messagePort": { + const port = createMessagePort(transferable.data); + ArrayPrototypePush(transferables, port); + ArrayPrototypePush(hostObjects, port); + break; + } + case "arrayBuffer": { + ArrayPrototypePush(transferredArrayBuffers, transferable.data); + const index = ArrayPrototypePush(transferables, null); + ArrayPrototypePush(arrayBufferIdsInTransferables, index); + break; } + default: + throw new TypeError("Unreachable"); } + } - const data = core.deserialize(messageData.data, { - hostObjects, - transferredArrayBuffers, - }); - - for (let i = 0; i < arrayBufferIdsInTransferables.length; ++i) { - const id = arrayBufferIdsInTransferables[i]; - transferables[id] = transferredArrayBuffers[i]; - } + const data = core.deserialize(messageData.data, { + hostObjects, + transferredArrayBuffers, + }); - return [data, transferables]; + for (let i = 0; i < arrayBufferIdsInTransferables.length; ++i) { + const id = arrayBufferIdsInTransferables[i]; + transferables[id] = transferredArrayBuffers[i]; } - /** - * @param {any} data - * @param {object[]} transferables - * @returns {globalThis.__bootstrap.messagePort.MessageData} - */ - function serializeJsMessageData(data, transferables) { - const transferredArrayBuffers = []; - for (let i = 0, j = 0; i < transferables.length; i++) { - const ab = transferables[i]; - if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, ab)) { - if (ab.byteLength === 0 && core.ops.op_arraybuffer_was_detached(ab)) { - throw new DOMException( - `ArrayBuffer at index ${j} is already detached`, - "DataCloneError", - ); - } - j++; - transferredArrayBuffers.push(ab); + return [data, transferables]; +} + +/** + * @param {any} data + * @param {object[]} transferables + * @returns {messagePort.MessageData} + */ +function serializeJsMessageData(data, transferables) { + const transferredArrayBuffers = []; + for (let i = 0, j = 0; i < transferables.length; i++) { + const ab = transferables[i]; + if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, ab)) { + if (ab.byteLength === 0 && ops.op_arraybuffer_was_detached(ab)) { + throw new DOMException( + `ArrayBuffer at index ${j} is already detached`, + "DataCloneError", + ); } + j++; + transferredArrayBuffers.push(ab); } + } - const serializedData = core.serialize(data, { - hostObjects: ArrayPrototypeFilter( - transferables, - (a) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, a), - ), - transferredArrayBuffers, - }, (err) => { - throw new DOMException(err, "DataCloneError"); - }); - - /** @type {globalThis.__bootstrap.messagePort.Transferable[]} */ - const serializedTransferables = []; + const serializedData = core.serialize(data, { + hostObjects: ArrayPrototypeFilter( + transferables, + (a) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, a), + ), + transferredArrayBuffers, + }, (err) => { + throw new DOMException(err, "DataCloneError"); + }); - let arrayBufferI = 0; - for (let i = 0; i < transferables.length; ++i) { - const transferable = transferables[i]; - if (ObjectPrototypeIsPrototypeOf(MessagePortPrototype, transferable)) { - webidl.assertBranded(transferable, MessagePortPrototype); - const id = transferable[_id]; - if (id === null) { - throw new DOMException( - "Can not transfer disentangled message port", - "DataCloneError", - ); - } - transferable[_id] = null; - ArrayPrototypePush(serializedTransferables, { - kind: "messagePort", - data: id, - }); - } else if ( - ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, transferable) - ) { - ArrayPrototypePush(serializedTransferables, { - kind: "arrayBuffer", - data: transferredArrayBuffers[arrayBufferI], - }); - arrayBufferI++; - } else { - throw new DOMException("Value not transferable", "DataCloneError"); + /** @type {messagePort.Transferable[]} */ + const serializedTransferables = []; + + let arrayBufferI = 0; + for (let i = 0; i < transferables.length; ++i) { + const transferable = transferables[i]; + if (ObjectPrototypeIsPrototypeOf(MessagePortPrototype, transferable)) { + webidl.assertBranded(transferable, MessagePortPrototype); + const id = transferable[_id]; + if (id === null) { + throw new DOMException( + "Can not transfer disentangled message port", + "DataCloneError", + ); } + transferable[_id] = null; + ArrayPrototypePush(serializedTransferables, { + kind: "messagePort", + data: id, + }); + } else if ( + ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, transferable) + ) { + ArrayPrototypePush(serializedTransferables, { + kind: "arrayBuffer", + data: transferredArrayBuffers[arrayBufferI], + }); + arrayBufferI++; + } else { + throw new DOMException("Value not transferable", "DataCloneError"); } - - return { - data: serializedData, - transferables: serializedTransferables, - }; } - webidl.converters.StructuredSerializeOptions = webidl - .createDictionaryConverter( - "StructuredSerializeOptions", - [ - { - key: "transfer", - converter: webidl.converters["sequence<object>"], - get defaultValue() { - return []; - }, - }, - ], - ); - - function structuredClone(value, options) { - const prefix = "Failed to execute 'structuredClone'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - options = webidl.converters.StructuredSerializeOptions(options, { - prefix, - context: "Argument 2", - }); - const messageData = serializeJsMessageData(value, options.transfer); - return deserializeJsMessageData(messageData)[0]; - } - - window.__bootstrap.messagePort = { - MessageChannel, - MessagePort, - MessagePortPrototype, - deserializeJsMessageData, - serializeJsMessageData, - structuredClone, + return { + data: serializedData, + transferables: serializedTransferables, }; -})(globalThis); +} + +webidl.converters.StructuredSerializeOptions = webidl + .createDictionaryConverter( + "StructuredSerializeOptions", + [ + { + key: "transfer", + converter: webidl.converters["sequence<object>"], + get defaultValue() { + return []; + }, + }, + ], + ); + +function structuredClone(value, options) { + const prefix = "Failed to execute 'structuredClone'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + options = webidl.converters.StructuredSerializeOptions(options, { + prefix, + context: "Argument 2", + }); + const messageData = serializeJsMessageData(value, options.transfer); + return deserializeJsMessageData(messageData)[0]; +} + +export { + deserializeJsMessageData, + MessageChannel, + MessagePort, + MessagePortPrototype, + serializeJsMessageData, + structuredClone, +}; diff --git a/ext/web/14_compression.js b/ext/web/14_compression.js index 338f8c803..680da757e 100644 --- a/ext/web/14_compression.js +++ b/ext/web/14_compression.js @@ -5,127 +5,120 @@ /// <reference path="./internal.d.ts" /> /// <reference path="./lib.deno_web.d.ts" /> -"use strict"; - -((window) => { - const core = window.Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { TransformStream } = window.__bootstrap.streams; - - webidl.converters.CompressionFormat = webidl.createEnumConverter( - "CompressionFormat", - [ - "deflate", - "deflate-raw", - "gzip", - ], - ); - - class CompressionStream { - #transform; - - constructor(format) { - const prefix = "Failed to construct 'CompressionStream'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - format = webidl.converters.CompressionFormat(format, { - prefix, - context: "Argument 1", - }); - - const rid = ops.op_compression_new(format, false); - - this.#transform = new TransformStream({ - transform(chunk, controller) { - chunk = webidl.converters.BufferSource(chunk, { - prefix, - context: "chunk", - }); - const output = ops.op_compression_write( - rid, - chunk, - ); - maybeEnqueue(controller, output); - }, - flush(controller) { - const output = ops.op_compression_finish(rid); - maybeEnqueue(controller, output); - }, - }); - - this[webidl.brand] = webidl.brand; - } - - get readable() { - webidl.assertBranded(this, CompressionStreamPrototype); - return this.#transform.readable; - } - - get writable() { - webidl.assertBranded(this, CompressionStreamPrototype); - return this.#transform.writable; - } +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { TransformStream } from "internal:ext/web/06_streams.js"; + +webidl.converters.CompressionFormat = webidl.createEnumConverter( + "CompressionFormat", + [ + "deflate", + "deflate-raw", + "gzip", + ], +); + +class CompressionStream { + #transform; + + constructor(format) { + const prefix = "Failed to construct 'CompressionStream'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + format = webidl.converters.CompressionFormat(format, { + prefix, + context: "Argument 1", + }); + + const rid = ops.op_compression_new(format, false); + + this.#transform = new TransformStream({ + transform(chunk, controller) { + chunk = webidl.converters.BufferSource(chunk, { + prefix, + context: "chunk", + }); + const output = ops.op_compression_write( + rid, + chunk, + ); + maybeEnqueue(controller, output); + }, + flush(controller) { + const output = ops.op_compression_finish(rid); + maybeEnqueue(controller, output); + }, + }); + + this[webidl.brand] = webidl.brand; } - webidl.configurePrototype(CompressionStream); - const CompressionStreamPrototype = CompressionStream.prototype; - - class DecompressionStream { - #transform; - - constructor(format) { - const prefix = "Failed to construct 'DecompressionStream'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - format = webidl.converters.CompressionFormat(format, { - prefix, - context: "Argument 1", - }); - - const rid = ops.op_compression_new(format, true); - - this.#transform = new TransformStream({ - transform(chunk, controller) { - chunk = webidl.converters.BufferSource(chunk, { - prefix, - context: "chunk", - }); - const output = ops.op_compression_write( - rid, - chunk, - ); - maybeEnqueue(controller, output); - }, - flush(controller) { - const output = ops.op_compression_finish(rid); - maybeEnqueue(controller, output); - }, - }); - - this[webidl.brand] = webidl.brand; - } - - get readable() { - webidl.assertBranded(this, DecompressionStreamPrototype); - return this.#transform.readable; - } - - get writable() { - webidl.assertBranded(this, DecompressionStreamPrototype); - return this.#transform.writable; - } + get readable() { + webidl.assertBranded(this, CompressionStreamPrototype); + return this.#transform.readable; } - function maybeEnqueue(controller, output) { - if (output && output.byteLength > 0) { - controller.enqueue(output); - } + get writable() { + webidl.assertBranded(this, CompressionStreamPrototype); + return this.#transform.writable; } +} + +webidl.configurePrototype(CompressionStream); +const CompressionStreamPrototype = CompressionStream.prototype; + +class DecompressionStream { + #transform; + + constructor(format) { + const prefix = "Failed to construct 'DecompressionStream'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + format = webidl.converters.CompressionFormat(format, { + prefix, + context: "Argument 1", + }); + + const rid = ops.op_compression_new(format, true); + + this.#transform = new TransformStream({ + transform(chunk, controller) { + chunk = webidl.converters.BufferSource(chunk, { + prefix, + context: "chunk", + }); + const output = ops.op_compression_write( + rid, + chunk, + ); + maybeEnqueue(controller, output); + }, + flush(controller) { + const output = ops.op_compression_finish(rid); + maybeEnqueue(controller, output); + }, + }); + + this[webidl.brand] = webidl.brand; + } + + get readable() { + webidl.assertBranded(this, DecompressionStreamPrototype); + return this.#transform.readable; + } + + get writable() { + webidl.assertBranded(this, DecompressionStreamPrototype); + return this.#transform.writable; + } +} + +function maybeEnqueue(controller, output) { + if (output && output.byteLength > 0) { + controller.enqueue(output); + } +} - webidl.configurePrototype(DecompressionStream); - const DecompressionStreamPrototype = DecompressionStream.prototype; +webidl.configurePrototype(DecompressionStream); +const DecompressionStreamPrototype = DecompressionStream.prototype; - window.__bootstrap.compression = { - CompressionStream, - DecompressionStream, - }; -})(globalThis); +export { CompressionStream, DecompressionStream }; diff --git a/ext/web/15_performance.js b/ext/web/15_performance.js index 9107ce75b..6a50f45f8 100644 --- a/ext/web/15_performance.js +++ b/ext/web/15_performance.js @@ -1,594 +1,594 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; - -((window) => { - const { - ArrayPrototypeFilter, - ArrayPrototypeFind, - ArrayPrototypePush, - ArrayPrototypeReverse, - ArrayPrototypeSlice, - ObjectKeys, - ObjectPrototypeIsPrototypeOf, - ReflectHas, - Symbol, - SymbolFor, - TypeError, - } = window.__bootstrap.primordials; - - const { webidl, structuredClone } = window.__bootstrap; - const consoleInternal = window.__bootstrap.console; - const { EventTarget } = window.__bootstrap.eventTarget; - const { opNow } = window.__bootstrap.timers; - const { DOMException } = window.__bootstrap.domException; - - const illegalConstructorKey = Symbol("illegalConstructorKey"); - const customInspect = SymbolFor("Deno.customInspect"); - let performanceEntries = []; - let timeOrigin; - - webidl.converters["PerformanceMarkOptions"] = webidl - .createDictionaryConverter( - "PerformanceMarkOptions", - [ - { - key: "detail", - converter: webidl.converters.any, - }, - { - key: "startTime", - converter: webidl.converters.DOMHighResTimeStamp, - }, - ], - ); - - webidl.converters["DOMString or DOMHighResTimeStamp"] = (V, opts) => { - if (webidl.type(V) === "Number" && V !== null) { - return webidl.converters.DOMHighResTimeStamp(V, opts); - } - return webidl.converters.DOMString(V, opts); - }; - - webidl.converters["PerformanceMeasureOptions"] = webidl - .createDictionaryConverter( - "PerformanceMeasureOptions", - [ - { - key: "detail", - converter: webidl.converters.any, - }, - { - key: "start", - converter: webidl.converters["DOMString or DOMHighResTimeStamp"], - }, - { - key: "duration", - converter: webidl.converters.DOMHighResTimeStamp, - }, - { - key: "end", - converter: webidl.converters["DOMString or DOMHighResTimeStamp"], - }, - ], - ); - webidl.converters["DOMString or PerformanceMeasureOptions"] = (V, opts) => { - if (webidl.type(V) === "Object" && V !== null) { - return webidl.converters["PerformanceMeasureOptions"](V, opts); - } - return webidl.converters.DOMString(V, opts); - }; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypeFilter, + ArrayPrototypeFind, + ArrayPrototypePush, + ArrayPrototypeReverse, + ArrayPrototypeSlice, + ObjectKeys, + ObjectPrototypeIsPrototypeOf, + ReflectHas, + Symbol, + SymbolFor, + TypeError, +} = primordials; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { structuredClone } from "internal:ext/web/02_structured_clone.js"; +import { createFilteredInspectProxy } from "internal:ext/console/02_console.js"; +import { EventTarget } from "internal:ext/web/02_event.js"; +import { opNow } from "internal:ext/web/02_timers.js"; +import DOMException from "internal:ext/web/01_dom_exception.js"; + +const illegalConstructorKey = Symbol("illegalConstructorKey"); +const customInspect = SymbolFor("Deno.customInspect"); +let performanceEntries = []; +let timeOrigin; + +webidl.converters["PerformanceMarkOptions"] = webidl + .createDictionaryConverter( + "PerformanceMarkOptions", + [ + { + key: "detail", + converter: webidl.converters.any, + }, + { + key: "startTime", + converter: webidl.converters.DOMHighResTimeStamp, + }, + ], + ); - function setTimeOrigin(origin) { - timeOrigin = origin; +webidl.converters["DOMString or DOMHighResTimeStamp"] = (V, opts) => { + if (webidl.type(V) === "Number" && V !== null) { + return webidl.converters.DOMHighResTimeStamp(V, opts); } + return webidl.converters.DOMString(V, opts); +}; + +webidl.converters["PerformanceMeasureOptions"] = webidl + .createDictionaryConverter( + "PerformanceMeasureOptions", + [ + { + key: "detail", + converter: webidl.converters.any, + }, + { + key: "start", + converter: webidl.converters["DOMString or DOMHighResTimeStamp"], + }, + { + key: "duration", + converter: webidl.converters.DOMHighResTimeStamp, + }, + { + key: "end", + converter: webidl.converters["DOMString or DOMHighResTimeStamp"], + }, + ], + ); - function findMostRecent( - name, - type, - ) { - return ArrayPrototypeFind( - ArrayPrototypeReverse(ArrayPrototypeSlice(performanceEntries)), - (entry) => entry.name === name && entry.entryType === type, - ); +webidl.converters["DOMString or PerformanceMeasureOptions"] = (V, opts) => { + if (webidl.type(V) === "Object" && V !== null) { + return webidl.converters["PerformanceMeasureOptions"](V, opts); } - - function convertMarkToTimestamp(mark) { - if (typeof mark === "string") { - const entry = findMostRecent(mark, "mark"); - if (!entry) { - throw new DOMException( - `Cannot find mark: "${mark}".`, - "SyntaxError", - ); - } - return entry.startTime; - } - if (mark < 0) { - throw new TypeError("Mark cannot be negative."); + return webidl.converters.DOMString(V, opts); +}; + +function setTimeOrigin(origin) { + timeOrigin = origin; +} + +function findMostRecent( + name, + type, +) { + return ArrayPrototypeFind( + ArrayPrototypeReverse(ArrayPrototypeSlice(performanceEntries)), + (entry) => entry.name === name && entry.entryType === type, + ); +} + +function convertMarkToTimestamp(mark) { + if (typeof mark === "string") { + const entry = findMostRecent(mark, "mark"); + if (!entry) { + throw new DOMException( + `Cannot find mark: "${mark}".`, + "SyntaxError", + ); } - return mark; + return entry.startTime; } - - function filterByNameType( - name, - type, - ) { - return ArrayPrototypeFilter( - performanceEntries, - (entry) => - (name ? entry.name === name : true) && - (type ? entry.entryType === type : true), - ); + if (mark < 0) { + throw new TypeError("Mark cannot be negative."); + } + return mark; +} + +function filterByNameType( + name, + type, +) { + return ArrayPrototypeFilter( + performanceEntries, + (entry) => + (name ? entry.name === name : true) && + (type ? entry.entryType === type : true), + ); +} + +const now = opNow; + +const _name = Symbol("[[name]]"); +const _entryType = Symbol("[[entryType]]"); +const _startTime = Symbol("[[startTime]]"); +const _duration = Symbol("[[duration]]"); +class PerformanceEntry { + [_name] = ""; + [_entryType] = ""; + [_startTime] = 0; + [_duration] = 0; + + get name() { + webidl.assertBranded(this, PerformanceEntryPrototype); + return this[_name]; } - const now = opNow; + get entryType() { + webidl.assertBranded(this, PerformanceEntryPrototype); + return this[_entryType]; + } - const _name = Symbol("[[name]]"); - const _entryType = Symbol("[[entryType]]"); - const _startTime = Symbol("[[startTime]]"); - const _duration = Symbol("[[duration]]"); - class PerformanceEntry { - [_name] = ""; - [_entryType] = ""; - [_startTime] = 0; - [_duration] = 0; + get startTime() { + webidl.assertBranded(this, PerformanceEntryPrototype); + return this[_startTime]; + } - get name() { - webidl.assertBranded(this, PerformanceEntryPrototype); - return this[_name]; - } + get duration() { + webidl.assertBranded(this, PerformanceEntryPrototype); + return this[_duration]; + } - get entryType() { - webidl.assertBranded(this, PerformanceEntryPrototype); - return this[_entryType]; + constructor( + name = null, + entryType = null, + startTime = null, + duration = null, + key = undefined, + ) { + if (key !== illegalConstructorKey) { + webidl.illegalConstructor(); } + this[webidl.brand] = webidl.brand; - get startTime() { - webidl.assertBranded(this, PerformanceEntryPrototype); - return this[_startTime]; - } + this[_name] = name; + this[_entryType] = entryType; + this[_startTime] = startTime; + this[_duration] = duration; + } - get duration() { - webidl.assertBranded(this, PerformanceEntryPrototype); - return this[_duration]; - } + toJSON() { + webidl.assertBranded(this, PerformanceEntryPrototype); + return { + name: this[_name], + entryType: this[_entryType], + startTime: this[_startTime], + duration: this[_duration], + }; + } - constructor( - name = null, - entryType = null, - startTime = null, - duration = null, - key = undefined, - ) { - if (key !== illegalConstructorKey) { - webidl.illegalConstructor(); - } - this[webidl.brand] = webidl.brand; + [customInspect](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + PerformanceEntryPrototype, + this, + ), + keys: [ + "name", + "entryType", + "startTime", + "duration", + ], + })); + } +} +webidl.configurePrototype(PerformanceEntry); +const PerformanceEntryPrototype = PerformanceEntry.prototype; - this[_name] = name; - this[_entryType] = entryType; - this[_startTime] = startTime; - this[_duration] = duration; - } +const _detail = Symbol("[[detail]]"); +class PerformanceMark extends PerformanceEntry { + [_detail] = null; - toJSON() { - webidl.assertBranded(this, PerformanceEntryPrototype); - return { - name: this[_name], - entryType: this[_entryType], - startTime: this[_startTime], - duration: this[_duration], - }; - } + get detail() { + webidl.assertBranded(this, PerformanceMarkPrototype); + return this[_detail]; + } - [customInspect](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - PerformanceEntryPrototype, - this, - ), - keys: [ - "name", - "entryType", - "startTime", - "duration", - ], - })); - } + get entryType() { + webidl.assertBranded(this, PerformanceMarkPrototype); + return "mark"; } - webidl.configurePrototype(PerformanceEntry); - const PerformanceEntryPrototype = PerformanceEntry.prototype; - const _detail = Symbol("[[detail]]"); - class PerformanceMark extends PerformanceEntry { - [_detail] = null; + constructor( + name, + options = {}, + ) { + const prefix = "Failed to construct 'PerformanceMark'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); - get detail() { - webidl.assertBranded(this, PerformanceMarkPrototype); - return this[_detail]; - } + name = webidl.converters.DOMString(name, { + prefix, + context: "Argument 1", + }); - get entryType() { - webidl.assertBranded(this, PerformanceMarkPrototype); - return "mark"; - } + options = webidl.converters.PerformanceMarkOptions(options, { + prefix, + context: "Argument 2", + }); - constructor( - name, - options = {}, - ) { - const prefix = "Failed to construct 'PerformanceMark'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); + const { detail = null, startTime = now() } = options; - name = webidl.converters.DOMString(name, { - prefix, - context: "Argument 1", - }); + super(name, "mark", startTime, 0, illegalConstructorKey); + this[webidl.brand] = webidl.brand; + if (startTime < 0) { + throw new TypeError("startTime cannot be negative"); + } + this[_detail] = structuredClone(detail); + } - options = webidl.converters.PerformanceMarkOptions(options, { - prefix, - context: "Argument 2", - }); + toJSON() { + webidl.assertBranded(this, PerformanceMarkPrototype); + return { + name: this.name, + entryType: this.entryType, + startTime: this.startTime, + duration: this.duration, + detail: this.detail, + }; + } - const { detail = null, startTime = now() } = options; + [customInspect](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(PerformanceMarkPrototype, this), + keys: [ + "name", + "entryType", + "startTime", + "duration", + "detail", + ], + })); + } +} +webidl.configurePrototype(PerformanceMark); +const PerformanceMarkPrototype = PerformanceMark.prototype; +class PerformanceMeasure extends PerformanceEntry { + [_detail] = null; + + get detail() { + webidl.assertBranded(this, PerformanceMeasurePrototype); + return this[_detail]; + } - super(name, "mark", startTime, 0, illegalConstructorKey); - this[webidl.brand] = webidl.brand; - if (startTime < 0) { - throw new TypeError("startTime cannot be negative"); - } - this[_detail] = structuredClone(detail); - } + get entryType() { + webidl.assertBranded(this, PerformanceMeasurePrototype); + return "measure"; + } - toJSON() { - webidl.assertBranded(this, PerformanceMarkPrototype); - return { - name: this.name, - entryType: this.entryType, - startTime: this.startTime, - duration: this.duration, - detail: this.detail, - }; + constructor( + name = null, + startTime = null, + duration = null, + detail = null, + key = undefined, + ) { + if (key !== illegalConstructorKey) { + webidl.illegalConstructor(); } - [customInspect](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(PerformanceMarkPrototype, this), - keys: [ - "name", - "entryType", - "startTime", - "duration", - "detail", - ], - })); - } + super(name, "measure", startTime, duration, key); + this[webidl.brand] = webidl.brand; + this[_detail] = structuredClone(detail); } - webidl.configurePrototype(PerformanceMark); - const PerformanceMarkPrototype = PerformanceMark.prototype; - class PerformanceMeasure extends PerformanceEntry { - [_detail] = null; - get detail() { - webidl.assertBranded(this, PerformanceMeasurePrototype); - return this[_detail]; - } + toJSON() { + webidl.assertBranded(this, PerformanceMeasurePrototype); + return { + name: this.name, + entryType: this.entryType, + startTime: this.startTime, + duration: this.duration, + detail: this.detail, + }; + } - get entryType() { - webidl.assertBranded(this, PerformanceMeasurePrototype); - return "measure"; + [customInspect](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + PerformanceMeasurePrototype, + this, + ), + keys: [ + "name", + "entryType", + "startTime", + "duration", + "detail", + ], + })); + } +} +webidl.configurePrototype(PerformanceMeasure); +const PerformanceMeasurePrototype = PerformanceMeasure.prototype; +class Performance extends EventTarget { + constructor(key = null) { + if (key != illegalConstructorKey) { + webidl.illegalConstructor(); } - constructor( - name = null, - startTime = null, - duration = null, - detail = null, - key = undefined, - ) { - if (key !== illegalConstructorKey) { - webidl.illegalConstructor(); - } + super(); + this[webidl.brand] = webidl.brand; + } - super(name, "measure", startTime, duration, key); - this[webidl.brand] = webidl.brand; - this[_detail] = structuredClone(detail); - } + get timeOrigin() { + webidl.assertBranded(this, PerformancePrototype); + return timeOrigin; + } - toJSON() { - webidl.assertBranded(this, PerformanceMeasurePrototype); - return { - name: this.name, - entryType: this.entryType, - startTime: this.startTime, - duration: this.duration, - detail: this.detail, - }; - } + clearMarks(markName = undefined) { + webidl.assertBranded(this, PerformancePrototype); + if (markName !== undefined) { + markName = webidl.converters.DOMString(markName, { + prefix: "Failed to execute 'clearMarks' on 'Performance'", + context: "Argument 1", + }); - [customInspect](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf( - PerformanceMeasurePrototype, - this, - ), - keys: [ - "name", - "entryType", - "startTime", - "duration", - "detail", - ], - })); + performanceEntries = ArrayPrototypeFilter( + performanceEntries, + (entry) => !(entry.name === markName && entry.entryType === "mark"), + ); + } else { + performanceEntries = ArrayPrototypeFilter( + performanceEntries, + (entry) => entry.entryType !== "mark", + ); } } - webidl.configurePrototype(PerformanceMeasure); - const PerformanceMeasurePrototype = PerformanceMeasure.prototype; - class Performance extends EventTarget { - constructor(key = null) { - if (key != illegalConstructorKey) { - webidl.illegalConstructor(); - } - super(); - this[webidl.brand] = webidl.brand; - } - - get timeOrigin() { - webidl.assertBranded(this, PerformancePrototype); - return timeOrigin; - } + clearMeasures(measureName = undefined) { + webidl.assertBranded(this, PerformancePrototype); + if (measureName !== undefined) { + measureName = webidl.converters.DOMString(measureName, { + prefix: "Failed to execute 'clearMeasures' on 'Performance'", + context: "Argument 1", + }); - clearMarks(markName = undefined) { - webidl.assertBranded(this, PerformancePrototype); - if (markName !== undefined) { - markName = webidl.converters.DOMString(markName, { - prefix: "Failed to execute 'clearMarks' on 'Performance'", - context: "Argument 1", - }); - - performanceEntries = ArrayPrototypeFilter( - performanceEntries, - (entry) => !(entry.name === markName && entry.entryType === "mark"), - ); - } else { - performanceEntries = ArrayPrototypeFilter( - performanceEntries, - (entry) => entry.entryType !== "mark", - ); - } + performanceEntries = ArrayPrototypeFilter( + performanceEntries, + (entry) => + !(entry.name === measureName && entry.entryType === "measure"), + ); + } else { + performanceEntries = ArrayPrototypeFilter( + performanceEntries, + (entry) => entry.entryType !== "measure", + ); } + } - clearMeasures(measureName = undefined) { - webidl.assertBranded(this, PerformancePrototype); - if (measureName !== undefined) { - measureName = webidl.converters.DOMString(measureName, { - prefix: "Failed to execute 'clearMeasures' on 'Performance'", - context: "Argument 1", - }); - - performanceEntries = ArrayPrototypeFilter( - performanceEntries, - (entry) => - !(entry.name === measureName && entry.entryType === "measure"), - ); - } else { - performanceEntries = ArrayPrototypeFilter( - performanceEntries, - (entry) => entry.entryType !== "measure", - ); - } - } + getEntries() { + webidl.assertBranded(this, PerformancePrototype); + return filterByNameType(); + } - getEntries() { - webidl.assertBranded(this, PerformancePrototype); - return filterByNameType(); - } + getEntriesByName( + name, + type = undefined, + ) { + webidl.assertBranded(this, PerformancePrototype); + const prefix = "Failed to execute 'getEntriesByName' on 'Performance'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); - getEntriesByName( - name, - type = undefined, - ) { - webidl.assertBranded(this, PerformancePrototype); - const prefix = "Failed to execute 'getEntriesByName' on 'Performance'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); + name = webidl.converters.DOMString(name, { + prefix, + context: "Argument 1", + }); - name = webidl.converters.DOMString(name, { + if (type !== undefined) { + type = webidl.converters.DOMString(type, { prefix, - context: "Argument 1", + context: "Argument 2", }); + } - if (type !== undefined) { - type = webidl.converters.DOMString(type, { - prefix, - context: "Argument 2", - }); - } + return filterByNameType(name, type); + } - return filterByNameType(name, type); - } + getEntriesByType(type) { + webidl.assertBranded(this, PerformancePrototype); + const prefix = "Failed to execute 'getEntriesByName' on 'Performance'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); - getEntriesByType(type) { - webidl.assertBranded(this, PerformancePrototype); - const prefix = "Failed to execute 'getEntriesByName' on 'Performance'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); + type = webidl.converters.DOMString(type, { + prefix, + context: "Argument 1", + }); - type = webidl.converters.DOMString(type, { - prefix, - context: "Argument 1", - }); + return filterByNameType(undefined, type); + } - return filterByNameType(undefined, type); - } + mark( + markName, + markOptions = {}, + ) { + webidl.assertBranded(this, PerformancePrototype); + const prefix = "Failed to execute 'mark' on 'Performance'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + + markName = webidl.converters.DOMString(markName, { + prefix, + context: "Argument 1", + }); + + markOptions = webidl.converters.PerformanceMarkOptions(markOptions, { + prefix, + context: "Argument 2", + }); + + // 3.1.1.1 If the global object is a Window object and markName uses the + // same name as a read only attribute in the PerformanceTiming interface, + // throw a SyntaxError. - not implemented + const entry = new PerformanceMark(markName, markOptions); + // 3.1.1.7 Queue entry - not implemented + ArrayPrototypePush(performanceEntries, entry); + return entry; + } - mark( - markName, - markOptions = {}, - ) { - webidl.assertBranded(this, PerformancePrototype); - const prefix = "Failed to execute 'mark' on 'Performance'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); + measure( + measureName, + startOrMeasureOptions = {}, + endMark = undefined, + ) { + webidl.assertBranded(this, PerformancePrototype); + const prefix = "Failed to execute 'measure' on 'Performance'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); - markName = webidl.converters.DOMString(markName, { - prefix, - context: "Argument 1", - }); + measureName = webidl.converters.DOMString(measureName, { + prefix, + context: "Argument 1", + }); - markOptions = webidl.converters.PerformanceMarkOptions(markOptions, { + startOrMeasureOptions = webidl.converters + ["DOMString or PerformanceMeasureOptions"](startOrMeasureOptions, { prefix, context: "Argument 2", }); - // 3.1.1.1 If the global object is a Window object and markName uses the - // same name as a read only attribute in the PerformanceTiming interface, - // throw a SyntaxError. - not implemented - const entry = new PerformanceMark(markName, markOptions); - // 3.1.1.7 Queue entry - not implemented - ArrayPrototypePush(performanceEntries, entry); - return entry; - } - - measure( - measureName, - startOrMeasureOptions = {}, - endMark = undefined, - ) { - webidl.assertBranded(this, PerformancePrototype); - const prefix = "Failed to execute 'measure' on 'Performance'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - - measureName = webidl.converters.DOMString(measureName, { + if (endMark !== undefined) { + endMark = webidl.converters.DOMString(endMark, { prefix, - context: "Argument 1", + context: "Argument 3", }); + } - startOrMeasureOptions = webidl.converters - ["DOMString or PerformanceMeasureOptions"](startOrMeasureOptions, { - prefix, - context: "Argument 2", - }); - - if (endMark !== undefined) { - endMark = webidl.converters.DOMString(endMark, { - prefix, - context: "Argument 3", - }); + if ( + startOrMeasureOptions && typeof startOrMeasureOptions === "object" && + ObjectKeys(startOrMeasureOptions).length > 0 + ) { + if (endMark) { + throw new TypeError("Options cannot be passed with endMark."); } - if ( - startOrMeasureOptions && typeof startOrMeasureOptions === "object" && - ObjectKeys(startOrMeasureOptions).length > 0 - ) { - if (endMark) { - throw new TypeError("Options cannot be passed with endMark."); - } - if ( - !ReflectHas(startOrMeasureOptions, "start") && - !ReflectHas(startOrMeasureOptions, "end") - ) { - throw new TypeError( - "A start or end mark must be supplied in options.", - ); - } - if ( - ReflectHas(startOrMeasureOptions, "start") && - ReflectHas(startOrMeasureOptions, "duration") && - ReflectHas(startOrMeasureOptions, "end") - ) { - throw new TypeError( - "Cannot specify start, end, and duration together in options.", - ); - } - } - let endTime; - if (endMark) { - endTime = convertMarkToTimestamp(endMark); - } else if ( - typeof startOrMeasureOptions === "object" && - ReflectHas(startOrMeasureOptions, "end") - ) { - endTime = convertMarkToTimestamp(startOrMeasureOptions.end); - } else if ( - typeof startOrMeasureOptions === "object" && - ReflectHas(startOrMeasureOptions, "start") && - ReflectHas(startOrMeasureOptions, "duration") + !ReflectHas(startOrMeasureOptions, "start") && + !ReflectHas(startOrMeasureOptions, "end") ) { - const start = convertMarkToTimestamp(startOrMeasureOptions.start); - const duration = convertMarkToTimestamp(startOrMeasureOptions.duration); - endTime = start + duration; - } else { - endTime = now(); + throw new TypeError( + "A start or end mark must be supplied in options.", + ); } - let startTime; if ( - typeof startOrMeasureOptions === "object" && - ReflectHas(startOrMeasureOptions, "start") - ) { - startTime = convertMarkToTimestamp(startOrMeasureOptions.start); - } else if ( - typeof startOrMeasureOptions === "object" && - ReflectHas(startOrMeasureOptions, "end") && - ReflectHas(startOrMeasureOptions, "duration") + ReflectHas(startOrMeasureOptions, "start") && + ReflectHas(startOrMeasureOptions, "duration") && + ReflectHas(startOrMeasureOptions, "end") ) { - const end = convertMarkToTimestamp(startOrMeasureOptions.end); - const duration = convertMarkToTimestamp(startOrMeasureOptions.duration); - startTime = end - duration; - } else if (typeof startOrMeasureOptions === "string") { - startTime = convertMarkToTimestamp(startOrMeasureOptions); - } else { - startTime = 0; + throw new TypeError( + "Cannot specify start, end, and duration together in options.", + ); } - const entry = new PerformanceMeasure( - measureName, - startTime, - endTime - startTime, - typeof startOrMeasureOptions === "object" - ? startOrMeasureOptions.detail ?? null - : null, - illegalConstructorKey, - ); - ArrayPrototypePush(performanceEntries, entry); - return entry; - } - - now() { - webidl.assertBranded(this, PerformancePrototype); - return now(); - } - - toJSON() { - webidl.assertBranded(this, PerformancePrototype); - return { - timeOrigin: this.timeOrigin, - }; } + let endTime; + if (endMark) { + endTime = convertMarkToTimestamp(endMark); + } else if ( + typeof startOrMeasureOptions === "object" && + ReflectHas(startOrMeasureOptions, "end") + ) { + endTime = convertMarkToTimestamp(startOrMeasureOptions.end); + } else if ( + typeof startOrMeasureOptions === "object" && + ReflectHas(startOrMeasureOptions, "start") && + ReflectHas(startOrMeasureOptions, "duration") + ) { + const start = convertMarkToTimestamp(startOrMeasureOptions.start); + const duration = convertMarkToTimestamp(startOrMeasureOptions.duration); + endTime = start + duration; + } else { + endTime = now(); + } + let startTime; + if ( + typeof startOrMeasureOptions === "object" && + ReflectHas(startOrMeasureOptions, "start") + ) { + startTime = convertMarkToTimestamp(startOrMeasureOptions.start); + } else if ( + typeof startOrMeasureOptions === "object" && + ReflectHas(startOrMeasureOptions, "end") && + ReflectHas(startOrMeasureOptions, "duration") + ) { + const end = convertMarkToTimestamp(startOrMeasureOptions.end); + const duration = convertMarkToTimestamp(startOrMeasureOptions.duration); + startTime = end - duration; + } else if (typeof startOrMeasureOptions === "string") { + startTime = convertMarkToTimestamp(startOrMeasureOptions); + } else { + startTime = 0; + } + const entry = new PerformanceMeasure( + measureName, + startTime, + endTime - startTime, + typeof startOrMeasureOptions === "object" + ? startOrMeasureOptions.detail ?? null + : null, + illegalConstructorKey, + ); + ArrayPrototypePush(performanceEntries, entry); + return entry; + } - [customInspect](inspect) { - return inspect(consoleInternal.createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(PerformancePrototype, this), - keys: [], - })); - } + now() { + webidl.assertBranded(this, PerformancePrototype); + return now(); } - webidl.configurePrototype(Performance); - const PerformancePrototype = Performance.prototype; - webidl.converters["Performance"] = webidl.createInterfaceConverter( - "Performance", - PerformancePrototype, - ); + toJSON() { + webidl.assertBranded(this, PerformancePrototype); + return { + timeOrigin: this.timeOrigin, + }; + } - window.__bootstrap.performance = { - PerformanceEntry, - PerformanceMark, - PerformanceMeasure, - Performance, - performance: new Performance(illegalConstructorKey), - setTimeOrigin, - }; -})(this); + [customInspect](inspect) { + return inspect(createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(PerformancePrototype, this), + keys: [], + })); + } +} +webidl.configurePrototype(Performance); +const PerformancePrototype = Performance.prototype; + +webidl.converters["Performance"] = webidl.createInterfaceConverter( + "Performance", + PerformancePrototype, +); + +const performance = new Performance(illegalConstructorKey); + +export { + Performance, + performance, + PerformanceEntry, + PerformanceMark, + PerformanceMeasure, + setTimeOrigin, +}; diff --git a/ext/web/benches/encoding.rs b/ext/web/benches/encoding.rs index 254ea4455..f8ad57c4f 100644 --- a/ext/web/benches/encoding.rs +++ b/ext/web/benches/encoding.rs @@ -29,11 +29,12 @@ fn setup() -> Vec<Extension> { deno_console::init(), deno_web::init::<Permissions>(BlobStore::default(), None), Extension::builder("bench_setup") - .js(vec![( - "setup", + .esm(vec![( + "internal:setup", r#" - const { TextDecoder } = globalThis.__bootstrap.encoding; - const hello12k = Deno.core.encode("hello world\n".repeat(1e3)); + import { TextDecoder } from "internal:ext/web/08_text_encoding.js"; + globalThis.TextDecoder = TextDecoder; + globalThis.hello12k = Deno.core.encode("hello world\n".repeat(1e3)); "#, )]) .state(|state| { diff --git a/ext/web/benches/timers_ops.rs b/ext/web/benches/timers_ops.rs index b28b1ae1d..a2af22982 100644 --- a/ext/web/benches/timers_ops.rs +++ b/ext/web/benches/timers_ops.rs @@ -28,9 +28,10 @@ fn setup() -> Vec<Extension> { deno_console::init(), deno_web::init::<Permissions>(BlobStore::default(), None), Extension::builder("bench_setup") - .js(vec![ - ("setup", r#" - const { setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers; + .esm(vec![ + ("internal:setup", r#" + import { setTimeout, handleTimerMacrotask } from "internal:ext/web/02_timers.js"; + globalThis.setTimeout = setTimeout; Deno.core.setMacrotaskCallback(handleTimerMacrotask); "#), ]) diff --git a/ext/web/internal.d.ts b/ext/web/internal.d.ts index 9bb89d98e..fe0c8ac07 100644 --- a/ext/web/internal.d.ts +++ b/ext/web/internal.d.ts @@ -1,120 +1,111 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file no-var - /// <reference no-default-lib="true" /> /// <reference lib="esnext" /> -declare namespace globalThis { - declare namespace __bootstrap { - declare var infra: { - collectSequenceOfCodepoints( - input: string, - position: number, - condition: (char: string) => boolean, - ): { - result: string; - position: number; - }; - ASCII_DIGIT: string[]; - ASCII_UPPER_ALPHA: string[]; - ASCII_LOWER_ALPHA: string[]; - ASCII_ALPHA: string[]; - ASCII_ALPHANUMERIC: string[]; - HTTP_TAB_OR_SPACE: string[]; - HTTP_WHITESPACE: string[]; - HTTP_TOKEN_CODE_POINT: string[]; - HTTP_TOKEN_CODE_POINT_RE: RegExp; - HTTP_QUOTED_STRING_TOKEN_POINT: string[]; - HTTP_QUOTED_STRING_TOKEN_POINT_RE: RegExp; - HTTP_TAB_OR_SPACE_PREFIX_RE: RegExp; - HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp; - HTTP_WHITESPACE_PREFIX_RE: RegExp; - HTTP_WHITESPACE_SUFFIX_RE: RegExp; - httpTrim(s: string): string; - regexMatcher(chars: string[]): string; - byteUpperCase(s: string): string; - byteLowerCase(s: string): string; - collectHttpQuotedString( - input: string, - position: number, - extractValue: boolean, - ): { - result: string; - position: number; - }; - forgivingBase64Encode(data: Uint8Array): string; - forgivingBase64Decode(data: string): Uint8Array; - serializeJSValueToJSONString(value: unknown): string; - }; - - declare var domException: { - DOMException: typeof DOMException; - }; +declare module "internal:ext/web/00_infra.js" { + function collectSequenceOfCodepoints( + input: string, + position: number, + condition: (char: string) => boolean, + ): { + result: string; + position: number; + }; + const ASCII_DIGIT: string[]; + const ASCII_UPPER_ALPHA: string[]; + const ASCII_LOWER_ALPHA: string[]; + const ASCII_ALPHA: string[]; + const ASCII_ALPHANUMERIC: string[]; + const HTTP_TAB_OR_SPACE: string[]; + const HTTP_WHITESPACE: string[]; + const HTTP_TOKEN_CODE_POINT: string[]; + const HTTP_TOKEN_CODE_POINT_RE: RegExp; + const HTTP_QUOTED_STRING_TOKEN_POINT: string[]; + const HTTP_QUOTED_STRING_TOKEN_POINT_RE: RegExp; + const HTTP_TAB_OR_SPACE_PREFIX_RE: RegExp; + const HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp; + const HTTP_WHITESPACE_PREFIX_RE: RegExp; + const HTTP_WHITESPACE_SUFFIX_RE: RegExp; + function httpTrim(s: string): string; + function regexMatcher(chars: string[]): string; + function byteUpperCase(s: string): string; + function byteLowerCase(s: string): string; + function collectHttpQuotedString( + input: string, + position: number, + extractValue: boolean, + ): { + result: string; + position: number; + }; + function forgivingBase64Encode(data: Uint8Array): string; + function forgivingBase64Decode(data: string): Uint8Array; + function serializeJSValueToJSONString(value: unknown): string; +} - declare namespace mimesniff { - declare interface MimeType { - type: string; - subtype: string; - parameters: Map<string, string>; - } - declare function parseMimeType(input: string): MimeType | null; - declare function essence(mimeType: MimeType): string; - declare function serializeMimeType(mimeType: MimeType): string; - declare function extractMimeType( - headerValues: string[] | null, - ): MimeType | null; - } +declare module "internal:ext/web/01_dom_exception.js" { + export = DOMException; +} - declare var eventTarget: { - EventTarget: typeof EventTarget; - }; +declare module "internal:ext/web/01_mimesniff.js" { + interface MimeType { + type: string; + subtype: string; + parameters: Map<string, string>; + } + function parseMimeType(input: string): MimeType | null; + function essence(mimeType: MimeType): string; + function serializeMimeType(mimeType: MimeType): string; + function extractMimeType( + headerValues: string[] | null, + ): MimeType | null; +} - declare var event: { - Event: typeof event; - ErrorEvent: typeof ErrorEvent; - CloseEvent: typeof CloseEvent; - MessageEvent: typeof MessageEvent; - CustomEvent: typeof CustomEvent; - ProgressEvent: typeof ProgressEvent; - PromiseRejectionEvent: typeof PromiseRejectionEvent; - reportError: typeof reportError; - }; +declare module "internal:ext/web/02_event.js" { + const EventTarget: typeof EventTarget; + const Event: typeof event; + const ErrorEvent: typeof ErrorEvent; + const CloseEvent: typeof CloseEvent; + const MessageEvent: typeof MessageEvent; + const CustomEvent: typeof CustomEvent; + const ProgressEvent: typeof ProgressEvent; + const PromiseRejectionEvent: typeof PromiseRejectionEvent; + const reportError: typeof reportError; +} - declare var location: { - getLocationHref(): string | undefined; - }; +declare module "internal:ext/web/12_location.js" { + function getLocationHref(): string | undefined; +} - declare var base64: { - atob(data: string): string; - btoa(data: string): string; - }; +declare module "internal:ext/web/05_base64.js" { + function atob(data: string): string; + function btoa(data: string): string; +} - declare var file: { - blobFromObjectUrl(url: string): Blob | null; - getParts(blob: Blob): string[]; - Blob: typeof Blob; - File: typeof File; - }; +declare module "internal:ext/web/09_file.js" { + function blobFromObjectUrl(url: string): Blob | null; + function getParts(blob: Blob): string[]; + const Blob: typeof Blob; + const File: typeof File; +} - declare var streams: { - ReadableStream: typeof ReadableStream; - isReadableStreamDisturbed(stream: ReadableStream): boolean; - createProxy<T>(stream: ReadableStream<T>): ReadableStream<T>; - }; +declare module "internal:ext/web/06_streams.js" { + const ReadableStream: typeof ReadableStream; + function isReadableStreamDisturbed(stream: ReadableStream): boolean; + function createProxy<T>(stream: ReadableStream<T>): ReadableStream<T>; +} - declare namespace messagePort { - declare type Transferable = { - kind: "messagePort"; - data: number; - } | { - kind: "arrayBuffer"; - data: number; - }; - declare interface MessageData { - data: Uint8Array; - transferables: Transferable[]; - } - } +declare module "internal:ext/web/13_message_port.js" { + type Transferable = { + kind: "messagePort"; + data: number; + } | { + kind: "arrayBuffer"; + data: number; + }; + interface MessageData { + data: Uint8Array; + transferables: Transferable[]; } } diff --git a/ext/web/lib.rs b/ext/web/lib.rs index c677bb8e9..4fcc06ef4 100644 --- a/ext/web/lib.rs +++ b/ext/web/lib.rs @@ -64,7 +64,7 @@ pub fn init<P: TimersPermission + 'static>( ) -> Extension { Extension::builder(env!("CARGO_PKG_NAME")) .dependencies(vec!["deno_webidl", "deno_console", "deno_url"]) - .js(include_js_files!( + .esm(include_js_files!( prefix "internal:ext/web", "00_infra.js", "01_dom_exception.js", |