diff --git a/core/internal.d.ts b/core/internal.d.ts index a3e8c9e4d3..c78310aeb6 100644 --- a/core/internal.d.ts +++ b/core/internal.d.ts @@ -588,6 +588,7 @@ declare namespace __bootstrap { export const MapLength: typeof Map.length; export const MapName: typeof Map.name; export const MapPrototype: typeof Map.prototype; + export const MapPrototypeGetSize: (map: Map) => number; export const MapPrototypeGet: UncurryThis; export const MapPrototypeSet: UncurryThis; export const MapPrototypeHas: UncurryThis; @@ -715,6 +716,7 @@ declare namespace __bootstrap { export const SetLength: typeof Set.length; export const SetName: typeof Set.name; export const SetPrototype: typeof Set.prototype; + export const SetPrototypeGetSize: (set: Set) => number; export const SetPrototypeHas: UncurryThis; export const SetPrototypeAdd: UncurryThis; export const SetPrototypeDelete: UncurryThis; @@ -866,6 +868,7 @@ declare namespace __bootstrap { export const SymbolLength: typeof Symbol.length; export const SymbolName: typeof Symbol.name; export const SymbolPrototype: typeof Symbol.prototype; + export const SymbolPrototypeGetDescription: (symbol: symbol) => string; export const SymbolFor: typeof Symbol.for; export const SymbolKeyFor: typeof Symbol.keyFor; export const SymbolAsyncIterator: typeof Symbol.asyncIterator; diff --git a/ext/console/02_console.js b/ext/console/02_console.js index 5547dd230a..85a0f784fe 100644 --- a/ext/console/02_console.js +++ b/ext/console/02_console.js @@ -64,6 +64,7 @@ const { SymbolPrototype, SymbolPrototypeToString, SymbolPrototypeValueOf, + SymbolPrototypeGetDescription, SymbolToStringTag, SymbolHasInstance, SymbolFor, @@ -662,7 +663,7 @@ function handleCircular(value, cyan) { } else { index = MapPrototypeGet(circular, value); if (index === undefined) { - index = circular.size + 1; + index = MapPrototypeGetSize(circular) + 1; MapPrototypeSet(circular, value, index); } } @@ -809,20 +810,17 @@ const QUOTE_SYMBOL_REG = new SafeRegExp(/^[a-zA-Z_][a-zA-Z_.0-9]*$/); // Surround a symbol's description in quotes when it is required (e.g the description has non printable characters). function maybeQuoteSymbol(symbol, inspectOptions) { - if (symbol.description === undefined) { + const description = SymbolPrototypeGetDescription(symbol); + + if (description === undefined) { return SymbolPrototypeToString(symbol); } - if ( - RegExpPrototypeTest( - QUOTE_SYMBOL_REG, - symbol.description, - ) - ) { + if (RegExpPrototypeTest(QUOTE_SYMBOL_REG, description)) { return SymbolPrototypeToString(symbol); } - return `Symbol(${quoteString(symbol.description, inspectOptions)})`; + return `Symbol(${quoteString(description, inspectOptions)})`; } const CTX_STACK = []; @@ -1191,8 +1189,8 @@ function inspectRawObject( symbolKeys, (s1, s2) => StringPrototypeLocaleCompare( - s1.description ?? "", - s2.description ?? "", + SymbolPrototypeGetDescription(s1) ?? "", + SymbolPrototypeGetDescription(s2) ?? "", ), ); } diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index a9f37b58fb..fe30a45589 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -13,15 +13,15 @@ import * as webidl from "ext:deno_webidl/00_webidl.js"; import DOMException from "ext:deno_web/01_dom_exception.js"; const { ArrayBufferPrototype, + ArrayBufferPrototypeSlice, + ArrayBufferPrototypeGetByteLength, ArrayBufferIsView, ArrayPrototypeEvery, ArrayPrototypeFind, ArrayPrototypeIncludes, - BigInt64ArrayPrototype, - BigUint64ArrayPrototype, - Int16ArrayPrototype, - Int32ArrayPrototype, - Int8ArrayPrototype, + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, JSONParse, JSONStringify, MathCeil, @@ -37,12 +37,12 @@ const { SymbolFor, SyntaxError, TypedArrayPrototypeSlice, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetByteOffset, + TypedArrayPrototypeGetSymbolToStringTag, TypeError, - Uint16ArrayPrototype, - Uint32ArrayPrototype, Uint8Array, - Uint8ArrayPrototype, - Uint8ClampedArrayPrototype, WeakMap, WeakMapPrototypeGet, WeakMapPrototypeSet, @@ -250,13 +250,7 @@ function normalizeAlgorithm(algorithm, op) { const idlValue = normalizedAlgorithm[member]; // 3. if (idlType === "BufferSource" && idlValue) { - normalizedAlgorithm[member] = TypedArrayPrototypeSlice( - new Uint8Array( - ArrayBufferIsView(idlValue) ? idlValue.buffer : idlValue, - idlValue.byteOffset ?? 0, - idlValue.byteLength, - ), - ); + normalizedAlgorithm[member] = copyBuffer(idlValue); } else if (idlType === "HashAlgorithmIdentifier") { normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, "digest"); } else if (idlType === "AlgorithmIdentifier") { @@ -273,10 +267,34 @@ function normalizeAlgorithm(algorithm, op) { * @returns {Uint8Array} */ function copyBuffer(input) { + if (ArrayBufferIsView(input)) { + if (TypedArrayPrototypeGetSymbolToStringTag(input) !== undefined) { + // TypedArray + return TypedArrayPrototypeSlice( + new Uint8Array( + TypedArrayPrototypeGetBuffer(/** @type {Uint8Array} */ (input)), + TypedArrayPrototypeGetByteOffset(/** @type {Uint8Array} */ (input)), + TypedArrayPrototypeGetByteLength(/** @type {Uint8Array} */ (input)), + ), + ); + } else { + // DataView + return TypedArrayPrototypeSlice( + new Uint8Array( + DataViewPrototypeGetBuffer(/** @type {DataView} */ (input)), + DataViewPrototypeGetByteOffset(/** @type {DataView} */ (input)), + DataViewPrototypeGetByteLength(/** @type {DataView} */ (input)), + ), + ); + } + } + // ArrayBuffer return TypedArrayPrototypeSlice( - ArrayBufferIsView(input) - ? new Uint8Array(input.buffer, input.byteOffset, input.byteLength) - : new Uint8Array(input), + new Uint8Array( + input, + 0, + ArrayBufferPrototypeGetByteLength(input), + ), ); } @@ -445,7 +463,7 @@ class SubtleCrypto { /** * @param {string} algorithm * @param {BufferSource} data - * @returns {Promise} + * @returns {Promise} */ async digest(algorithm, data) { webidl.assertBranded(this, SubtleCryptoPrototype); @@ -470,7 +488,7 @@ class SubtleCrypto { data, ); - return result.buffer; + return TypedArrayPrototypeGetBuffer(result); } /** @@ -596,13 +614,13 @@ class SubtleCrypto { }, data); // 6. - return plainText.buffer; + return TypedArrayPrototypeGetBuffer(plainText); } case "AES-CBC": { normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); // 1. - if (normalizedAlgorithm.iv.byteLength !== 16) { + if (TypedArrayPrototypeGetByteLength(normalizedAlgorithm.iv) !== 16) { throw new DOMException( "Counter must be 16 bytes", "OperationError", @@ -617,13 +635,15 @@ class SubtleCrypto { }, data); // 6. - return plainText.buffer; + return TypedArrayPrototypeGetBuffer(plainText); } case "AES-CTR": { normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter); // 1. - if (normalizedAlgorithm.counter.byteLength !== 16) { + if ( + TypedArrayPrototypeGetByteLength(normalizedAlgorithm.counter) !== 16 + ) { throw new DOMException( "Counter vector must be 16 bytes", "OperationError", @@ -650,7 +670,7 @@ class SubtleCrypto { }, data); // 4. - return cipherText.buffer; + return TypedArrayPrototypeGetBuffer(cipherText); } case "AES-GCM": { normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); @@ -671,7 +691,10 @@ class SubtleCrypto { } // 2. - if (data.byteLength < normalizedAlgorithm.tagLength / 8) { + if ( + TypedArrayPrototypeGetByteLength(data) < + normalizedAlgorithm.tagLength / 8 + ) { throw new DOMException( "Tag length overflows ciphertext", "OperationError", @@ -682,7 +705,7 @@ class SubtleCrypto { if ( ArrayPrototypeIncludes( [12, 16], - normalizedAlgorithm.iv.byteLength, + TypedArrayPrototypeGetByteLength(normalizedAlgorithm.iv), ) === undefined ) { throw new DOMException( @@ -693,12 +716,13 @@ class SubtleCrypto { // 4. if (normalizedAlgorithm.additionalData !== undefined) { - if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { - throw new DOMException( - "Additional data too large", - "OperationError", - ); - } + // NOTE: over the size of Number.MAX_SAFE_INTEGER is not available in V8 + // if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { + // throw new DOMException( + // "Additional data too large", + // "OperationError", + // ); + // } normalizedAlgorithm.additionalData = copyBuffer( normalizedAlgorithm.additionalData, ); @@ -716,7 +740,7 @@ class SubtleCrypto { }, data); // 9. - return plaintext.buffer; + return TypedArrayPrototypeGetBuffer(plaintext); } default: throw new DOMException("Not implemented", "NotSupportedError"); @@ -789,7 +813,7 @@ class SubtleCrypto { hash: hashAlgorithm, }, data); - return signature.buffer; + return TypedArrayPrototypeGetBuffer(signature); } case "RSA-PSS": { // 1. @@ -809,7 +833,7 @@ class SubtleCrypto { saltLength: normalizedAlgorithm.saltLength, }, data); - return signature.buffer; + return TypedArrayPrototypeGetBuffer(signature); } case "ECDSA": { // 1. @@ -846,7 +870,7 @@ class SubtleCrypto { namedCurve, }, data); - return signature.buffer; + return TypedArrayPrototypeGetBuffer(signature); } case "HMAC": { const hashAlgorithm = key[_algorithm].hash.name; @@ -857,7 +881,7 @@ class SubtleCrypto { hash: hashAlgorithm, }, data); - return signature.buffer; + return TypedArrayPrototypeGetBuffer(signature); } case "Ed25519": { // 1. @@ -877,7 +901,7 @@ class SubtleCrypto { "OperationError", ); } - return signature.buffer; + return TypedArrayPrototypeGetBuffer(signature); } } @@ -1471,7 +1495,7 @@ class SubtleCrypto { }, bytes); // 4. - return cipherText.buffer; + return TypedArrayPrototypeGetBuffer(cipherText); } default: { throw new DOMException( @@ -1607,7 +1631,7 @@ class SubtleCrypto { }, wrappedKey); // 4. - key = plainText.buffer; + key = TypedArrayPrototypeGetBuffer(plainText); break; } default: { @@ -2127,7 +2151,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { hash: { name: normalizedAlgorithm.hash.name, }, - length: keyData.byteLength * 8, + length: TypedArrayPrototypeGetByteLength(keyData) * 8, }; // 5, 11-13. @@ -2589,7 +2613,7 @@ function exportKeyAES( // 1. const data = innerKey.data; // 2. - return data.buffer; + return TypedArrayPrototypeGetBuffer(data); } case "jwk": { // 1-2. @@ -2664,7 +2688,10 @@ function importKeyAES( case "raw": { // 2. if ( - !ArrayPrototypeIncludes([128, 192, 256], keyData.byteLength * 8) + !ArrayPrototypeIncludes( + [128, 192, 256], + TypedArrayPrototypeGetByteLength(keyData) * 8, + ) ) { throw new DOMException("Invalid key length", "Datarror"); } @@ -2699,7 +2726,7 @@ function importKeyAES( data = rawData.data; // 5. - switch (data.byteLength * 8) { + switch (TypedArrayPrototypeGetByteLength(data) * 8) { case 128: if ( jwk.alg !== undefined && @@ -2789,7 +2816,7 @@ function importKeyAES( // 4-7. const algorithm = { name: algorithmName, - length: data.byteLength * 8, + length: TypedArrayPrototypeGetByteLength(data) * 8, }; const key = constructKey( @@ -2956,7 +2983,7 @@ function importKeyHMAC( } // 5. - let length = data.byteLength * 8; + let length = TypedArrayPrototypeGetByteLength(data) * 8; // 6. if (length === 0) { throw new DOMException("Key length is zero", "DataError"); @@ -3856,11 +3883,12 @@ function exportKeyHMAC(format, key, innerKey) { // 3. case "raw": { const bits = innerKey.data; - for (let _i = 7 & (8 - bits.length % 8); _i > 0; _i--) { - bits.push(0); - } + // TODO(petamoriken): Uint8Array doesn't have push method + // for (let _i = 7 & (8 - bits.length % 8); _i > 0; _i--) { + // bits.push(0); + // } // 4-5. - return bits.buffer; + return TypedArrayPrototypeGetBuffer(bits); } case "jwk": { // 1-2. @@ -3929,7 +3957,7 @@ function exportKeyRSA(format, key, innerKey) { }, innerKey); // 3. - return data.buffer; + return TypedArrayPrototypeGetBuffer(data); } case "spki": { // 1. @@ -3947,7 +3975,7 @@ function exportKeyRSA(format, key, innerKey) { }, innerKey); // 3. - return data.buffer; + return TypedArrayPrototypeGetBuffer(data); } case "jwk": { // 1-2. @@ -4053,7 +4081,7 @@ function exportKeyEd25519(format, key, innerKey) { } // 2-3. - return innerKey.buffer; + return TypedArrayPrototypeGetBuffer(innerKey); } case "spki": { // 1. @@ -4065,7 +4093,7 @@ function exportKeyEd25519(format, key, innerKey) { } const spkiDer = ops.op_export_spki_ed25519(innerKey); - return spkiDer.buffer; + return TypedArrayPrototypeGetBuffer(spkiDer); } case "pkcs8": { // 1. @@ -4080,7 +4108,7 @@ function exportKeyEd25519(format, key, innerKey) { new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), ); pkcs8Der[15] = 0x20; - return pkcs8Der.buffer; + return TypedArrayPrototypeGetBuffer(pkcs8Der); } case "jwk": { const x = key[_type] === "private" @@ -4116,7 +4144,7 @@ function exportKeyX25519(format, key, innerKey) { } // 2-3. - return innerKey.buffer; + return TypedArrayPrototypeGetBuffer(innerKey); } case "spki": { // 1. @@ -4128,7 +4156,7 @@ function exportKeyX25519(format, key, innerKey) { } const spkiDer = ops.op_export_spki_x25519(innerKey); - return spkiDer.buffer; + return TypedArrayPrototypeGetBuffer(spkiDer); } case "pkcs8": { // 1. @@ -4143,7 +4171,7 @@ function exportKeyX25519(format, key, innerKey) { new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]), ); pkcs8Der[15] = 0x20; - return pkcs8Der.buffer; + return TypedArrayPrototypeGetBuffer(pkcs8Der); } case "jwk": { if (key[_type] === "private") { @@ -4182,7 +4210,7 @@ function exportKeyEC(format, key, innerKey) { format: "raw", }, innerKey); - return data.buffer; + return TypedArrayPrototypeGetBuffer(data); } case "pkcs8": { // 1. @@ -4200,7 +4228,7 @@ function exportKeyEC(format, key, innerKey) { format: "pkcs8", }, innerKey); - return data.buffer; + return TypedArrayPrototypeGetBuffer(data); } case "spki": { // 1. @@ -4218,7 +4246,7 @@ function exportKeyEC(format, key, innerKey) { format: "spki", }, innerKey); - return data.buffer; + return TypedArrayPrototypeGetBuffer(data); } case "jwk": { if (key[_algorithm].name == "ECDSA") { @@ -4370,7 +4398,7 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) { length, }, normalizedAlgorithm.salt); - return buf.buffer; + return TypedArrayPrototypeGetBuffer(buf); } case "ECDH": { // 1. @@ -4421,11 +4449,15 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) { // 8. if (length === null) { - return buf.buffer; - } else if (buf.buffer.byteLength * 8 < length) { + return TypedArrayPrototypeGetBuffer(buf); + } else if (TypedArrayPrototypeGetByteLength(buf) * 8 < length) { throw new DOMException("Invalid length", "OperationError"); } else { - return buf.buffer.slice(0, MathCeil(length / 8)); + return ArrayBufferPrototypeSlice( + TypedArrayPrototypeGetBuffer(buf), + 0, + MathCeil(length / 8), + ); } } else { throw new DOMException("Not implemented", "NotSupportedError"); @@ -4452,7 +4484,7 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) { length, }, normalizedAlgorithm.salt); - return buf.buffer; + return TypedArrayPrototypeGetBuffer(buf); } case "X25519": { // 1. @@ -4490,13 +4522,17 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) { // 7. if (length === null) { - return secret.buffer; + return TypedArrayPrototypeGetBuffer(secret); } else if ( - secret.buffer.byteLength * 8 < length + TypedArrayPrototypeGetByteLength(secret) * 8 < length ) { throw new DOMException("Invalid length", "OperationError"); } else { - return secret.buffer.slice(0, MathCeil(length / 8)); + return ArrayBufferPrototypeSlice( + TypedArrayPrototypeGetBuffer(secret), + 0, + MathCeil(length / 8), + ); } } default: @@ -4535,13 +4571,13 @@ async function encrypt(normalizedAlgorithm, key, data) { }, data); // 6. - return cipherText.buffer; + return TypedArrayPrototypeGetBuffer(cipherText); } case "AES-CBC": { normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); // 1. - if (normalizedAlgorithm.iv.byteLength !== 16) { + if (TypedArrayPrototypeGetByteLength(normalizedAlgorithm.iv) !== 16) { throw new DOMException( "Initialization vector must be 16 bytes", "OperationError", @@ -4557,13 +4593,15 @@ async function encrypt(normalizedAlgorithm, key, data) { }, data); // 4. - return cipherText.buffer; + return TypedArrayPrototypeGetBuffer(cipherText); } case "AES-CTR": { normalizedAlgorithm.counter = copyBuffer(normalizedAlgorithm.counter); // 1. - if (normalizedAlgorithm.counter.byteLength !== 16) { + if ( + TypedArrayPrototypeGetByteLength(normalizedAlgorithm.counter) !== 16 + ) { throw new DOMException( "Counter vector must be 16 bytes", "OperationError", @@ -4590,13 +4628,13 @@ async function encrypt(normalizedAlgorithm, key, data) { }, data); // 4. - return cipherText.buffer; + return TypedArrayPrototypeGetBuffer(cipherText); } case "AES-GCM": { normalizedAlgorithm.iv = copyBuffer(normalizedAlgorithm.iv); // 1. - if (data.byteLength > (2 ** 39) - 256) { + if (TypedArrayPrototypeGetByteLength(data) > (2 ** 39) - 256) { throw new DOMException( "Plaintext too large", "OperationError", @@ -4608,7 +4646,7 @@ async function encrypt(normalizedAlgorithm, key, data) { if ( ArrayPrototypeIncludes( [12, 16], - normalizedAlgorithm.iv.byteLength, + TypedArrayPrototypeGetByteLength(normalizedAlgorithm.iv), ) === undefined ) { throw new DOMException( @@ -4618,14 +4656,15 @@ async function encrypt(normalizedAlgorithm, key, data) { } // 3. - if (normalizedAlgorithm.additionalData !== undefined) { - if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { - throw new DOMException( - "Additional data too large", - "OperationError", - ); - } - } + // NOTE: over the size of Number.MAX_SAFE_INTEGER is not available in V8 + // if (normalizedAlgorithm.additionalData !== undefined) { + // if (normalizedAlgorithm.additionalData.byteLength > (2 ** 64) - 1) { + // throw new DOMException( + // "Additional data too large", + // "OperationError", + // ); + // } + // } // 4. if (normalizedAlgorithm.tagLength == undefined) { @@ -4658,7 +4697,7 @@ async function encrypt(normalizedAlgorithm, key, data) { }, data); // 8. - return cipherText.buffer; + return TypedArrayPrototypeGetBuffer(cipherText); } default: throw new DOMException("Not implemented", "NotSupportedError"); @@ -4673,50 +4712,43 @@ class Crypto { webidl.illegalConstructor(); } - getRandomValues(arrayBufferView) { + getRandomValues(typedArray) { webidl.assertBranded(this, CryptoPrototype); const prefix = "Failed to execute 'getRandomValues' on 'Crypto'"; webidl.requiredArguments(arguments.length, 1, { prefix }); // Fast path for Uint8Array - if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, arrayBufferView)) { - ops.op_crypto_get_random_values(arrayBufferView); - return arrayBufferView; + const tag = TypedArrayPrototypeGetSymbolToStringTag(typedArray); + if (tag === "Uint8Array") { + ops.op_crypto_get_random_values(typedArray); + return typedArray; } - arrayBufferView = webidl.converters.ArrayBufferView(arrayBufferView, { + typedArray = webidl.converters.ArrayBufferView(typedArray, { prefix, context: "Argument 1", }); - if ( - !( - ObjectPrototypeIsPrototypeOf(Int8ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf( - Uint8ClampedArrayPrototype, - arrayBufferView, - ) || - ObjectPrototypeIsPrototypeOf(Int16ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Uint16ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Int32ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf(Uint32ArrayPrototype, arrayBufferView) || - ObjectPrototypeIsPrototypeOf( - BigInt64ArrayPrototype, - arrayBufferView, - ) || - ObjectPrototypeIsPrototypeOf(BigUint64ArrayPrototype, arrayBufferView) - ) - ) { - throw new DOMException( - "The provided ArrayBufferView is not an integer array type", - "TypeMismatchError", - ); + switch (tag) { + case "Int8Array": + case "Uint8ClampedArray": + case "Int16Array": + case "Uint16Array": + case "Int32Array": + case "Uint32Array": + case "BigInt64Array": + case "BigUint64Array": + break; + default: + throw new DOMException( + "The provided ArrayBufferView is not an integer array type", + "TypeMismatchError", + ); } const ui8 = new Uint8Array( - arrayBufferView.buffer, - arrayBufferView.byteOffset, - arrayBufferView.byteLength, + TypedArrayPrototypeGetBuffer(typedArray), + TypedArrayPrototypeGetByteOffset(typedArray), + TypedArrayPrototypeGetByteLength(typedArray), ); ops.op_crypto_get_random_values(ui8); - return arrayBufferView; + return typedArray; } randomUUID() { diff --git a/ext/fetch/22_body.js b/ext/fetch/22_body.js index dd11df2a27..9dbd58fa4b 100644 --- a/ext/fetch/22_body.js +++ b/ext/fetch/22_body.js @@ -38,17 +38,24 @@ import { const primordials = globalThis.__bootstrap.primordials; const { ArrayBufferPrototype, + ArrayBufferPrototypeGetByteLength, ArrayBufferIsView, ArrayPrototypeMap, + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, JSONParse, ObjectDefineProperties, ObjectPrototypeIsPrototypeOf, // TODO(lucacasonato): add SharedArrayBuffer to primordials // SharedArrayBufferPrototype + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetByteOffset, + TypedArrayPrototypeGetSymbolToStringTag, TypedArrayPrototypeSlice, TypeError, Uint8Array, - Uint8ArrayPrototype, } = primordials; /** @@ -328,7 +335,7 @@ function mixinBody(prototype, bodySymbol, mimeTypeSymbol) { function packageData(bytes, type, mimeType) { switch (type) { case "ArrayBuffer": - return chunkToU8(bytes).buffer; + return TypedArrayPrototypeGetBuffer(chunkToU8(bytes)); case "Blob": return new Blob([bytes], { type: mimeType !== null ? mimesniff.serializeMimeType(mimeType) : "", @@ -385,22 +392,45 @@ function extractBody(object) { if (object.type.length !== 0) { contentType = object.type; } - } else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, object)) { - // Fast(er) path for common case of Uint8Array - const copy = TypedArrayPrototypeSlice(object, 0, object.byteLength); - source = copy; - } else if ( - ArrayBufferIsView(object) || - ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, object) - ) { - const u8 = ArrayBufferIsView(object) - ? new Uint8Array( - object.buffer, - object.byteOffset, - object.byteLength, - ) - : new Uint8Array(object); - const copy = TypedArrayPrototypeSlice(u8, 0, u8.byteLength); + } else if (ArrayBufferIsView(object)) { + const tag = TypedArrayPrototypeGetSymbolToStringTag(object); + if (tag === "Uint8Array") { + // Fast(er) path for common case of Uint8Array + const copy = TypedArrayPrototypeSlice( + object, + TypedArrayPrototypeGetByteOffset(/** @type {Uint8Array} */ (object)), + TypedArrayPrototypeGetByteLength(/** @type {Uint8Array} */ (object)), + ); + source = copy; + } else if (tag !== undefined) { + // TypedArray + const copy = TypedArrayPrototypeSlice( + new Uint8Array( + TypedArrayPrototypeGetBuffer(/** @type {Uint8Array} */ (object)), + TypedArrayPrototypeGetByteOffset(/** @type {Uint8Array} */ (object)), + TypedArrayPrototypeGetByteLength(/** @type {Uint8Array} */ (object)), + ), + ); + source = copy; + } else { + // DataView + const copy = TypedArrayPrototypeSlice( + new Uint8Array( + DataViewPrototypeGetBuffer(/** @type {DataView} */ (object)), + DataViewPrototypeGetByteOffset(/** @type {DataView} */ (object)), + DataViewPrototypeGetByteLength(/** @type {DataView} */ (object)), + ), + ); + source = copy; + } + } else if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, object)) { + const copy = TypedArrayPrototypeSlice( + new Uint8Array( + object, + 0, + ArrayBufferPrototypeGetByteLength(object), + ), + ); source = copy; } else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, object)) { const res = formDataToBlob(object); @@ -426,9 +456,9 @@ function extractBody(object) { // no observable side-effect for users so far, but could change stream = { body: source, consumed: false }; length = null; // NOTE: string length != byte length - } else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, source)) { + } else if (TypedArrayPrototypeGetSymbolToStringTag(source) === "Uint8Array") { stream = { body: source, consumed: false }; - length = source.byteLength; + length = TypedArrayPrototypeGetByteLength(source); } const body = new InnerBody(stream); body.source = source; diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 3664ae73be..ea75df65cd 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -4,13 +4,19 @@ const core = globalThis.Deno.core; const ops = core.ops; const primordials = globalThis.__bootstrap.primordials; const { + ArrayBufferIsView, + ArrayBufferPrototypeGetByteLength, ArrayPrototypeMap, ArrayPrototypeJoin, + DataViewPrototypeGetByteLength, ObjectDefineProperty, ObjectPrototypeHasOwnProperty, ObjectPrototypeIsPrototypeOf, Number, NumberIsSafeInteger, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetSymbolToStringTag, TypeError, Uint8Array, Int32Array, @@ -29,11 +35,27 @@ const { } = primordials; import { pathFromURL } from "ext:deno_web/00_infra.js"; +/** + * @param {BufferSource} source + * @returns {number} + */ +function getBufferSourceByteLength(source) { + if (ArrayBufferIsView(source)) { + if (TypedArrayPrototypeGetSymbolToStringTag(source) !== undefined) { + // TypedArray + return TypedArrayPrototypeGetByteLength(source); + } else { + // DataView + return DataViewPrototypeGetByteLength(source); + } + } + return ArrayBufferPrototypeGetByteLength(source); +} const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); const U32_BUFFER = new Uint32Array(2); -const U64_BUFFER = new BigUint64Array(U32_BUFFER.buffer); -const I64_BUFFER = new BigInt64Array(U32_BUFFER.buffer); +const U64_BUFFER = new BigUint64Array(TypedArrayPrototypeGetBuffer(U32_BUFFER)); +const I64_BUFFER = new BigInt64Array(TypedArrayPrototypeGetBuffer(U32_BUFFER)); class UnsafePointerView { pointer; @@ -164,7 +186,7 @@ class UnsafePointerView { this.pointer, offset, destination, - destination.byteLength, + getBufferSourceByteLength(destination), ); } @@ -173,13 +195,15 @@ class UnsafePointerView { pointer, offset, destination, - destination.byteLength, + getBufferSourceByteLength(destination), ); } } const OUT_BUFFER = new Uint32Array(2); -const OUT_BUFFER_64 = new BigInt64Array(OUT_BUFFER.buffer); +const OUT_BUFFER_64 = new BigInt64Array( + TypedArrayPrototypeGetBuffer(OUT_BUFFER), +); const POINTER_TO_BUFFER_WEAK_MAP = new WeakMap(); class UnsafePointer { static create(value) { @@ -492,8 +516,8 @@ class DynamicLibrary { const call = this.symbols[symbol]; const parameters = symbols[symbol].parameters; const vi = new Int32Array(2); - const vui = new Uint32Array(vi.buffer); - const b = new BigInt64Array(vi.buffer); + const vui = new Uint32Array(TypedArrayPrototypeGetBuffer(vi)); + const b = new BigInt64Array(TypedArrayPrototypeGetBuffer(vi)); const params = ArrayPrototypeJoin( ArrayPrototypeMap(parameters, (_, index) => `p${index}`), diff --git a/ext/flash/01_http.js b/ext/flash/01_http.js index 4d0112c5be..fe503ed05d 100644 --- a/ext/flash/01_http.js +++ b/ext/flash/01_http.js @@ -31,11 +31,12 @@ const { PromisePrototypeCatch, PromisePrototypeThen, SafePromiseAll, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetSymbolToStringTag, TypedArrayPrototypeSet, TypedArrayPrototypeSubarray, TypeError, Uint8Array, - Uint8ArrayPrototype, } = primordials; const statusCodes = { @@ -188,9 +189,15 @@ function http1Response( str += body ?? ""; } else { const head = core.encode(str); - const response = new Uint8Array(head.byteLength + bodyLen); + const response = new Uint8Array( + TypedArrayPrototypeGetByteLength(head) + bodyLen, + ); TypedArrayPrototypeSet(response, head, 0); - TypedArrayPrototypeSet(response, body, head.byteLength); + TypedArrayPrototypeSet( + response, + body, + TypedArrayPrototypeGetByteLength(head), + ); return response; } @@ -303,7 +310,7 @@ async function handleResponse( } isStreamingResponseBody = !( typeof respBody === "string" || - ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, respBody) + TypedArrayPrototypeGetSymbolToStringTag(respBody) === "Uint8Array" ); } else { if (innerResp.body.streamOrStatic.consumed === true) { @@ -318,7 +325,9 @@ async function handleResponse( const ws = resp[_ws]; if (isStreamingResponseBody === false) { - const length = respBody.byteLength || core.byteLength(respBody); + const length = typeof respBody === "string" + ? core.byteLength(respBody) + : TypedArrayPrototypeGetByteLength(respBody); const responseStr = http1Response( method, innerResp.status ?? 200, @@ -394,8 +403,10 @@ async function handleResponse( innerResp.status ?? 200, innerResp.headerList, null, + // deno-lint-ignore prefer-primordials respBody.byteLength, ), + // deno-lint-ignore prefer-primordials respBody.byteLength, false, respondFast, @@ -722,7 +733,7 @@ function createRequestBodyStream(serverId, token) { token, ); if (!firstRead) return null; - let firstEnqueued = firstRead.byteLength == 0; + let firstEnqueued = TypedArrayPrototypeGetByteLength(firstRead) === 0; return new ReadableStream({ type: "bytes", diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index 5004169805..1421de9eb1 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -137,7 +137,10 @@ function readDir(path) { ); return { async *[SymbolAsyncIterator]() { - yield* await array; + const dir = await array; + for (let i = 0; i < dir.length; ++i) { + yield dir[i]; + } }, }; } diff --git a/ext/io/12_io.js b/ext/io/12_io.js index e177117358..2a825e7f65 100644 --- a/ext/io/12_io.js +++ b/ext/io/12_io.js @@ -17,6 +17,8 @@ const { MathMin, TypedArrayPrototypeSubarray, TypedArrayPrototypeSet, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, } = primordials; const DEFAULT_BUFFER_SIZE = 32 * 1024; @@ -131,7 +133,10 @@ async function readAllInner(r, options) { const buf = new Uint8Array(READ_PER_ITER); const read = await r.read(buf); if (typeof read == "number") { - ArrayPrototypePush(buffers, new Uint8Array(buf.buffer, 0, read)); + ArrayPrototypePush( + buffers, + new Uint8Array(TypedArrayPrototypeGetBuffer(buf), 0, read), + ); } else { break; } @@ -160,7 +165,7 @@ function readAllSync(r) { function concatBuffers(buffers) { let totalLen = 0; for (let i = 0; i < buffers.length; ++i) { - totalLen += buffers[i].byteLength; + totalLen += TypedArrayPrototypeGetByteLength(buffers[i]); } const contents = new Uint8Array(totalLen); @@ -169,7 +174,7 @@ function concatBuffers(buffers) { for (let i = 0; i < buffers.length; ++i) { const buf = buffers[i]; TypedArrayPrototypeSet(contents, buf, n); - n += buf.byteLength; + n += TypedArrayPrototypeGetByteLength(buf); } return contents; diff --git a/ext/web/02_timers.js b/ext/web/02_timers.js index c224be884e..753848af19 100644 --- a/ext/web/02_timers.js +++ b/ext/web/02_timers.js @@ -19,6 +19,7 @@ const { PromisePrototypeThen, SafeArrayIterator, SymbolFor, + TypedArrayPrototypeGetBuffer, TypeError, indirectEval, } = primordials; @@ -27,7 +28,7 @@ import { reportException } from "ext:deno_web/02_event.js"; import { assert } from "ext:deno_web/00_infra.js"; const hrU8 = new Uint8Array(8); -const hr = new Uint32Array(hrU8.buffer); +const hr = new Uint32Array(TypedArrayPrototypeGetBuffer(hrU8)); function opNow() { ops.op_now(hrU8); return (hr[0] * 1000 + hr[1] / 1e6); diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js index 35d8f15e28..135a200ac4 100644 --- a/ext/web/06_streams.js +++ b/ext/web/06_streams.js @@ -20,6 +20,7 @@ const primordials = globalThis.__bootstrap.primordials; const { ArrayBuffer, ArrayBufferPrototype, + ArrayBufferPrototypeGetByteLength, ArrayBufferIsView, ArrayPrototypeMap, ArrayPrototypePush, @@ -67,6 +68,7 @@ const { TypedArrayPrototypeGetByteOffset, TypedArrayPrototypeGetSymbolToStringTag, TypedArrayPrototypeSet, + TypedArrayPrototypeSlice, Uint8Array, Uint16Array, Uint32Array, @@ -208,7 +210,12 @@ function uponPromise(promise, onFulfilled, onRejected) { * @returns {boolean} */ function isDetachedBuffer(O) { - return O.byteLength === 0 && ops.op_arraybuffer_was_detached(O); + // deno-lint-ignore prefer-primordials + if (ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, O)) { + return false; + } + return ArrayBufferPrototypeGetByteLength(O) === 0 && + ops.op_arraybuffer_was_detached(O); } /** @@ -237,6 +244,21 @@ function transferArrayBuffer(O) { return ops.op_transfer_arraybuffer(O); } +/** + * @param {ArrayBufferLike} O + * @returns {number} + */ +function getArrayBufferByteLength(O) { + // deno-lint-ignore prefer-primordials + if (ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, O)) { + // TODO(petamoriken): use primordials + // deno-lint-ignore prefer-primordials + return O.byteLength; + } else { + return ArrayBufferPrototypeGetByteLength(O); + } +} + /** * @param {ArrayBufferView} O * @returns {Uint8Array} @@ -244,9 +266,25 @@ function transferArrayBuffer(O) { 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); + if (TypedArrayPrototypeGetSymbolToStringTag(O) !== undefined) { + // TypedArray + return TypedArrayPrototypeSlice( + new Uint8Array( + TypedArrayPrototypeGetBuffer(/** @type {Uint8Array} */ (O)), + TypedArrayPrototypeGetByteOffset(/** @type {Uint8Array} */ (O)), + TypedArrayPrototypeGetByteLength(/** @type {Uint8Array} */ (O)), + ), + ); + } else { + // DataView + return TypedArrayPrototypeSlice( + new Uint8Array( + DataViewPrototypeGetBuffer(/** @type {DataView} */ (O)), + DataViewPrototypeGetByteOffset(/** @type {DataView} */ (O)), + DataViewPrototypeGetByteLength(/** @type {DataView} */ (O)), + ), + ); + } } const _abortAlgorithm = Symbol("[[abortAlgorithm]]"); @@ -695,7 +733,7 @@ function readableStreamForRid(rid, autoClose = true) { if (controller[_readAll] === true) { // fast path for tee'd streams consuming body const chunk = await core.readAll(rid); - if (chunk.byteLength > 0) { + if (TypedArrayPrototypeGetByteLength(chunk) > 0) { controller.enqueue(chunk); } controller.close(); @@ -870,7 +908,7 @@ async function readableStreamCollectIntoUint8Array(stream) { } ArrayPrototypePush(chunks, chunk); - totalLength += chunk.byteLength; + totalLength += TypedArrayPrototypeGetByteLength(chunk); } const finalBuffer = new Uint8Array(totalLength); @@ -878,7 +916,7 @@ async function readableStreamCollectIntoUint8Array(stream) { for (let i = 0; i < chunks.length; ++i) { const chunk = chunks[i]; TypedArrayPrototypeSet(finalBuffer, chunk, offset); - offset += chunk.byteLength; + offset += TypedArrayPrototypeGetByteLength(chunk); } return finalBuffer; } @@ -1092,7 +1130,25 @@ function readableByteStreamControllerEnqueue(controller, chunk) { return; } - const { buffer, byteOffset, byteLength } = chunk; + let buffer, byteLength, byteOffset; + if (TypedArrayPrototypeGetSymbolToStringTag(chunk) === undefined) { + buffer = DataViewPrototypeGetBuffer(/** @type {DataView} */ (chunk)); + byteLength = DataViewPrototypeGetByteLength( + /** @type {DataView} */ (chunk), + ); + byteOffset = DataViewPrototypeGetByteOffset( + /** @type {DataView} */ (chunk), + ); + } else { + buffer = TypedArrayPrototypeGetBuffer(/** @type {Uint8Array}} */ (chunk)); + byteLength = TypedArrayPrototypeGetByteLength( + /** @type {Uint8Array} */ (chunk), + ); + byteOffset = TypedArrayPrototypeGetByteOffset( + /** @type {Uint8Array} */ (chunk), + ); + } + if (isDetachedBuffer(buffer)) { throw new TypeError( "chunk's buffer is detached and so cannot be enqueued", @@ -1101,6 +1157,7 @@ function readableByteStreamControllerEnqueue(controller, chunk) { const transferredBuffer = transferArrayBuffer(buffer); if (controller[_pendingPullIntos].length !== 0) { const firstPendingPullInto = controller[_pendingPullIntos][0]; + // deno-lint-ignore prefer-primordials if (isDetachedBuffer(firstPendingPullInto.buffer)) { throw new TypeError( "The BYOB request's buffer has been detached and so cannot be filled with an enqueued chunk", @@ -1108,6 +1165,7 @@ function readableByteStreamControllerEnqueue(controller, chunk) { } readableByteStreamControllerInvalidateBYOBRequest(controller); firstPendingPullInto.buffer = transferArrayBuffer( + // deno-lint-ignore prefer-primordials firstPendingPullInto.buffer, ); if (firstPendingPullInto.readerType === "none") { @@ -1219,7 +1277,9 @@ function readableByteStreamControllerEnqueueDetachedPullIntoToQueue( if (pullIntoDescriptor.bytesFilled > 0) { readableByteStreamControllerEnqueueClonedChunkToQueue( controller, + // deno-lint-ignore prefer-primordials pullIntoDescriptor.buffer, + // deno-lint-ignore prefer-primordials pullIntoDescriptor.byteOffset, pullIntoDescriptor.bytesFilled, ); @@ -1238,8 +1298,11 @@ function readableByteStreamControllerGetBYOBRequest(controller) { ) { const firstDescriptor = controller[_pendingPullIntos][0]; const view = new Uint8Array( + // deno-lint-ignore prefer-primordials firstDescriptor.buffer, + // deno-lint-ignore prefer-primordials firstDescriptor.byteOffset + firstDescriptor.bytesFilled, + // deno-lint-ignore prefer-primordials firstDescriptor.byteLength - firstDescriptor.bytesFilled, ); const byobRequest = webidl.createBranded(ReadableStreamBYOBRequest); @@ -1753,7 +1816,7 @@ function readableByteStreamControllerPullInto( /** @type {PullIntoDescriptor} */ const pullIntoDescriptor = { buffer, - bufferByteLength: buffer.byteLength, + bufferByteLength: getArrayBufferByteLength(buffer), byteOffset, byteLength, bytesFilled: 0, @@ -1769,7 +1832,9 @@ function readableByteStreamControllerPullInto( } if (stream[_state] === "closed") { const emptyView = new ctor( + // deno-lint-ignore prefer-primordials pullIntoDescriptor.buffer, + // deno-lint-ignore prefer-primordials pullIntoDescriptor.byteOffset, 0, ); @@ -1828,11 +1893,13 @@ function readableByteStreamControllerRespond(controller, bytesWritten) { } if ( (firstDescriptor.bytesFilled + bytesWritten) > + // deno-lint-ignore prefer-primordials firstDescriptor.byteLength ) { throw new RangeError("bytesWritten out of range"); } } + // deno-lint-ignore prefer-primordials firstDescriptor.buffer = transferArrayBuffer(firstDescriptor.buffer); readableByteStreamControllerRespondInternal(controller, bytesWritten); } @@ -1850,6 +1917,7 @@ function readableByteStreamControllerRespondInReadableState( ) { assert( (pullIntoDescriptor.bytesFilled + bytesWritten) <= + // deno-lint-ignore prefer-primordials pullIntoDescriptor.byteLength, ); readableByteStreamControllerFillHeadPullIntoDescriptor( @@ -1874,10 +1942,12 @@ function readableByteStreamControllerRespondInReadableState( const remainderSize = pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize; if (remainderSize > 0) { + // deno-lint-ignore prefer-primordials const end = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; readableByteStreamControllerEnqueueClonedChunkToQueue( controller, + // deno-lint-ignore prefer-primordials pullIntoDescriptor.buffer, end - remainderSize, remainderSize, @@ -1903,6 +1973,7 @@ function readableByteStreamControllerRespondInternal( bytesWritten, ) { const firstDescriptor = controller[_pendingPullIntos][0]; + // deno-lint-ignore prefer-primordials assert(canTransferArrayBuffer(firstDescriptor.buffer)); readableByteStreamControllerInvalidateBYOBRequest(controller); const state = controller[_stream][_state]; @@ -1994,47 +2065,57 @@ function readableByteStreamControllerCommitPullIntoDescriptor( */ function readableByteStreamControllerRespondWithNewView(controller, view) { assert(controller[_pendingPullIntos].length !== 0); - assert(!isDetachedBuffer(view.buffer)); + + let buffer, byteLength, byteOffset; + if (TypedArrayPrototypeGetSymbolToStringTag(view) === undefined) { + buffer = DataViewPrototypeGetBuffer(/** @type {DataView} */ (view)); + byteLength = DataViewPrototypeGetByteLength(/** @type {DataView} */ (view)); + byteOffset = DataViewPrototypeGetByteOffset(/** @type {DataView} */ (view)); + } else { + buffer = TypedArrayPrototypeGetBuffer(/** @type {Uint8Array}} */ (view)); + byteLength = TypedArrayPrototypeGetByteLength( + /** @type {Uint8Array} */ (view), + ); + byteOffset = TypedArrayPrototypeGetByteOffset( + /** @type {Uint8Array} */ (view), + ); + } + assert(!isDetachedBuffer(buffer)); const firstDescriptor = controller[_pendingPullIntos][0]; const state = controller[_stream][_state]; if (state === "closed") { - if (view.byteLength !== 0) { + if (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) { + if (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 - ) { + // deno-lint-ignore prefer-primordials + if (firstDescriptor.byteOffset + firstDescriptor.bytesFilled !== byteOffset) { throw new RangeError( "The region specified by view does not match byobRequest", ); } - if (firstDescriptor.bufferByteLength !== view.buffer.byteLength) { + if (firstDescriptor.bufferByteLength !== getArrayBufferByteLength(buffer)) { throw new RangeError( "The buffer of view has different capacity than byobRequest", ); } - if ( - (firstDescriptor.bytesFilled + view.byteLength) > - firstDescriptor.byteLength - ) { + // deno-lint-ignore prefer-primordials + if (firstDescriptor.bytesFilled + byteLength > firstDescriptor.byteLength) { throw new RangeError( "The region specified by view is larger than byobRequest", ); } - const viewByteLength = view.byteLength; - firstDescriptor.buffer = transferArrayBuffer(view.buffer); - readableByteStreamControllerRespondInternal(controller, viewByteLength); + firstDescriptor.buffer = transferArrayBuffer(buffer); + readableByteStreamControllerRespondInternal(controller, byteLength); } /** @@ -2060,6 +2141,7 @@ function readableByteStreamControllerFillPullIntoDescriptorFromQueue( (pullIntoDescriptor.bytesFilled % elementSize); const maxBytesToCopy = MathMin( controller[_queueTotalSize], + // deno-lint-ignore prefer-primordials pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled, ); const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy; @@ -2076,23 +2158,29 @@ function readableByteStreamControllerFillPullIntoDescriptorFromQueue( const headOfQueue = queue[0]; const bytesToCopy = MathMin( totalBytesToCopyRemaining, + // deno-lint-ignore prefer-primordials headOfQueue.byteLength, ); + // deno-lint-ignore prefer-primordials const destStart = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; const destBuffer = new Uint8Array( + // deno-lint-ignore prefer-primordials pullIntoDescriptor.buffer, destStart, bytesToCopy, ); const srcBuffer = new Uint8Array( + // deno-lint-ignore prefer-primordials headOfQueue.buffer, + // deno-lint-ignore prefer-primordials headOfQueue.byteOffset, bytesToCopy, ); destBuffer.set(srcBuffer); + // deno-lint-ignore prefer-primordials if (headOfQueue.byteLength === bytesToCopy) { ArrayPrototypeShift(queue); } else { @@ -2126,11 +2214,15 @@ function readableByteStreamControllerFillReadRequestFromQueue( ) { assert(controller[_queueTotalSize] > 0); const entry = ArrayPrototypeShift(controller[_queue]); + // deno-lint-ignore prefer-primordials controller[_queueTotalSize] -= entry.byteLength; readableByteStreamControllerHandleQueueDrain(controller); const view = new Uint8Array( + // deno-lint-ignore prefer-primordials entry.buffer, + // deno-lint-ignore prefer-primordials entry.byteOffset, + // deno-lint-ignore prefer-primordials entry.byteLength, ); readRequest.chunkSteps(view); @@ -2164,11 +2256,14 @@ function readableByteStreamControllerConvertPullIntoDescriptor( ) { const bytesFilled = pullIntoDescriptor.bytesFilled; const elementSize = pullIntoDescriptor.elementSize; + // deno-lint-ignore prefer-primordials assert(bytesFilled <= pullIntoDescriptor.byteLength); assert((bytesFilled % elementSize) === 0); + // deno-lint-ignore prefer-primordials const buffer = transferArrayBuffer(pullIntoDescriptor.buffer); return new pullIntoDescriptor.viewConstructor( buffer, + // deno-lint-ignore prefer-primordials pullIntoDescriptor.byteOffset, bytesFilled / elementSize, ); @@ -3029,7 +3124,17 @@ function readableByteStreamTee(stream) { readableByteStreamControllerClose(otherBranch[_controller]); } if (chunk !== undefined) { - assert(chunk.byteLength === 0); + let byteLength; + if (TypedArrayPrototypeGetSymbolToStringTag(chunk) === undefined) { + byteLength = DataViewPrototypeGetByteLength( + /** @type {DataView} */ (chunk), + ); + } else { + byteLength = TypedArrayPrototypeGetByteLength( + /** @type {Uint8Array} */ (chunk), + ); + } + assert(byteLength === 0); if (!byobCanceled) { readableByteStreamControllerRespondWithNewView( byobBranch[_controller], @@ -4644,6 +4749,7 @@ function initializeByteLengthSizeFunction(globalObject) { if (WeakMapPrototypeHas(byteSizeFunctionWeakMap, globalObject)) { return; } + // deno-lint-ignore prefer-primordials const size = (chunk) => chunk.byteLength; WeakMapPrototypeSet(byteSizeFunctionWeakMap, globalObject, size); } @@ -5098,17 +5204,29 @@ class ReadableStreamBYOBReader { return PromiseReject(err); } - if (view.byteLength === 0) { + let buffer, byteLength; + if (TypedArrayPrototypeGetSymbolToStringTag(view) === undefined) { + buffer = DataViewPrototypeGetBuffer(/** @type {DataView} */ (view)); + byteLength = DataViewPrototypeGetByteLength( + /** @type {DataView} */ (view), + ); + } else { + buffer = TypedArrayPrototypeGetBuffer(/** @type {Uint8Array} */ (view)); + byteLength = TypedArrayPrototypeGetByteLength( + /** @type {Uint8Array} */ (view), + ); + } + if (byteLength === 0) { return PromiseReject( new TypeError("view must have non-zero byteLength"), ); } - if (view.buffer.byteLength === 0) { + if (getArrayBufferByteLength(buffer) === 0) { return PromiseReject( new TypeError("view's buffer must have non-zero byteLength"), ); } - if (isDetachedBuffer(view.buffer)) { + if (isDetachedBuffer(buffer)) { return PromiseReject( new TypeError("view's buffer has been detached"), ); @@ -5213,13 +5331,22 @@ class ReadableStreamBYOBRequest { if (this[_controller] === undefined) { throw new TypeError("This BYOB request has been invalidated"); } - if (isDetachedBuffer(this[_view].buffer)) { + + let buffer, byteLength; + if (TypedArrayPrototypeGetSymbolToStringTag(this[_view]) === undefined) { + buffer = DataViewPrototypeGetBuffer(this[_view]); + byteLength = DataViewPrototypeGetByteLength(this[_view]); + } else { + buffer = TypedArrayPrototypeGetBuffer(this[_view]); + byteLength = TypedArrayPrototypeGetByteLength(this[_view]); + } + if (isDetachedBuffer(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); + assert(byteLength > 0); + assert(getArrayBufferByteLength(buffer) > 0); readableByteStreamControllerRespond(this[_controller], bytesWritten); } @@ -5236,7 +5363,14 @@ class ReadableStreamBYOBRequest { if (this[_controller] === undefined) { throw new TypeError("This BYOB request has been invalidated"); } - if (isDetachedBuffer(view.buffer)) { + + let buffer; + if (TypedArrayPrototypeGetSymbolToStringTag(view) === undefined) { + buffer = DataViewPrototypeGetBuffer(view); + } else { + buffer = TypedArrayPrototypeGetBuffer(view); + } + if (isDetachedBuffer(buffer)) { throw new TypeError( "The given view's buffer has been detached and so cannot be used as a response", ); @@ -5320,13 +5454,25 @@ class ReadableByteStreamController { prefix, context: arg1, }); - if (chunk.byteLength === 0) { + let buffer, byteLength; + if (TypedArrayPrototypeGetSymbolToStringTag(chunk) === undefined) { + buffer = DataViewPrototypeGetBuffer(/** @type {DataView} */ (chunk)); + byteLength = DataViewPrototypeGetByteLength( + /** @type {DataView} */ (chunk), + ); + } else { + buffer = TypedArrayPrototypeGetBuffer(/** @type {Uint8Array} */ (chunk)); + byteLength = TypedArrayPrototypeGetByteLength( + /** @type {Uint8Array} */ (chunk), + ); + } + if (byteLength === 0) { throw webidl.makeException(TypeError, "length must be non-zero", { prefix, context: arg1, }); } - if (chunk.buffer.byteLength === 0) { + if (getArrayBufferByteLength(buffer) === 0) { throw webidl.makeException( TypeError, "buffer length must be non-zero", diff --git a/ext/web/08_text_encoding.js b/ext/web/08_text_encoding.js index c6c75874a3..2e19c3d1ff 100644 --- a/ext/web/08_text_encoding.js +++ b/ext/web/08_text_encoding.js @@ -14,6 +14,9 @@ const ops = core.ops; import * as webidl from "ext:deno_webidl/00_webidl.js"; const primordials = globalThis.__bootstrap.primordials; const { + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, PromiseReject, PromiseResolve, // TODO(lucacasonato): add SharedArrayBuffer to primordials @@ -21,6 +24,10 @@ const { StringPrototypeCharCodeAt, StringPrototypeSlice, TypedArrayPrototypeSubarray, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetByteOffset, + TypedArrayPrototypeGetSymbolToStringTag, Uint8Array, ObjectPrototypeIsPrototypeOf, ArrayBufferIsView, @@ -104,13 +111,27 @@ class TextDecoder { } try { + /** @type {ArrayBufferLike} */ + let buffer = input; + if (ArrayBufferIsView(input)) { + if (TypedArrayPrototypeGetSymbolToStringTag(input) !== undefined) { + // TypedArray + buffer = TypedArrayPrototypeGetBuffer( + /** @type {Uint8Array} */ (input), + ); + } else { + // DataView + buffer = DataViewPrototypeGetBuffer(/** @type {DataView} */ (input)); + } + } + // 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, + buffer, ) ) { // We clone the data into a non-shared ArrayBuffer so we can pass it @@ -118,13 +139,27 @@ class TextDecoder { // `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, - ); + if (TypedArrayPrototypeGetSymbolToStringTag(input) !== undefined) { + // TypedArray + input = new Uint8Array( + buffer, + TypedArrayPrototypeGetByteOffset( + /** @type {Uint8Array} */ (input), + ), + TypedArrayPrototypeGetByteLength( + /** @type {Uint8Array} */ (input), + ), + ); + } else { + // DataView + input = new Uint8Array( + buffer, + DataViewPrototypeGetByteOffset(/** @type {DataView} */ (input)), + DataViewPrototypeGetByteLength(/** @type {DataView} */ (input)), + ); + } } else { - input = new Uint8Array(input); + input = new Uint8Array(buffer); } } diff --git a/ext/web/09_file.js b/ext/web/09_file.js index 5ebef8f9d5..1ecebe8a85 100644 --- a/ext/web/09_file.js +++ b/ext/web/09_file.js @@ -18,9 +18,13 @@ const primordials = globalThis.__bootstrap.primordials; const { ArrayBufferPrototype, ArrayBufferPrototypeSlice, + ArrayBufferPrototypeGetByteLength, ArrayBufferIsView, ArrayPrototypePush, AsyncGeneratorPrototypeNext, + DataViewPrototypeGetBuffer, + DataViewPrototypeGetByteLength, + DataViewPrototypeGetByteOffset, Date, DatePrototypeGetTime, FinalizationRegistry, @@ -37,6 +41,10 @@ const { Symbol, SymbolFor, TypedArrayPrototypeSet, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetByteOffset, + TypedArrayPrototypeGetSymbolToStringTag, TypeError, Uint8Array, } = primordials; @@ -100,6 +108,7 @@ function convertLineEndingsToNative(s) { /** @param {(BlobReference | Blob)[]} parts */ async function* toIterator(parts) { for (let i = 0; i < parts.length; ++i) { + // deno-lint-ignore prefer-primordials yield* parts[i].stream(); } } @@ -120,15 +129,31 @@ function processBlobParts(parts, endings) { if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, element)) { const chunk = new Uint8Array(ArrayBufferPrototypeSlice(element, 0)); ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); - size += element.byteLength; + size += ArrayBufferPrototypeGetByteLength(element); } else if (ArrayBufferIsView(element)) { - const chunk = new Uint8Array( - element.buffer, - element.byteOffset, - element.byteLength, - ); - size += element.byteLength; - ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); + if (TypedArrayPrototypeGetSymbolToStringTag(element) !== undefined) { + // TypedArray + const chunk = new Uint8Array( + TypedArrayPrototypeGetBuffer(/** @type {Uint8Array} */ (element)), + TypedArrayPrototypeGetByteOffset(/** @type {Uint8Array} */ (element)), + TypedArrayPrototypeGetByteLength(/** @type {Uint8Array} */ (element)), + ); + size += TypedArrayPrototypeGetByteLength( + /** @type {Uint8Array} */ (element), + ); + ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); + } else { + // DataView + const chunk = new Uint8Array( + DataViewPrototypeGetBuffer(/** @type {DataView} */ (element)), + DataViewPrototypeGetByteOffset(/** @type {DataView} */ (element)), + DataViewPrototypeGetByteLength(/** @type {DataView} */ (element)), + ); + size += DataViewPrototypeGetByteLength( + /** @type {DataView} */ (element), + ); + ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); + } } else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, element)) { ArrayPrototypePush(processedParts, element); size += element.size; @@ -136,7 +161,7 @@ function processBlobParts(parts, endings) { const chunk = core.encode( endings == "native" ? convertLineEndingsToNative(element) : element, ); - size += chunk.byteLength; + size += TypedArrayPrototypeGetByteLength(chunk); ArrayPrototypePush(processedParts, BlobReference.fromUint8Array(chunk)); } else { throw new TypeError("Unreachable code (invalid element type)"); @@ -341,7 +366,7 @@ class Blob { partIterator, ); if (done) return controller.close(); - if (value.byteLength > 0) { + if (TypedArrayPrototypeGetByteLength(value) > 0) { return controller.enqueue(value); } } @@ -368,7 +393,7 @@ class Blob { partIterator, ); if (done) break; - const byteLength = value.byteLength; + const byteLength = TypedArrayPrototypeGetByteLength(value); if (byteLength > 0) { TypedArrayPrototypeSet(bytes, value, offset); offset += byteLength; @@ -383,7 +408,7 @@ class Blob { async arrayBuffer() { webidl.assertBranded(this, BlobPrototype); const buf = await this.#u8Array(this.size); - return buf.buffer; + return TypedArrayPrototypeGetBuffer(buf); } [SymbolFor("Deno.customInspect")](inspect) { @@ -554,7 +579,7 @@ class BlobReference { */ static fromUint8Array(data) { const id = ops.op_blob_create_part(data); - return new BlobReference(id, data.byteLength); + return new BlobReference(id, TypedArrayPrototypeGetByteLength(data)); } /** diff --git a/ext/web/10_filereader.js b/ext/web/10_filereader.js index 5dd2d5c3af..524a3fe51c 100644 --- a/ext/web/10_filereader.js +++ b/ext/web/10_filereader.js @@ -27,14 +27,15 @@ const { MapPrototypeGet, MapPrototypeSet, ObjectDefineProperty, - ObjectPrototypeIsPrototypeOf, queueMicrotask, SafeArrayIterator, Symbol, TypedArrayPrototypeSet, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetSymbolToStringTag, TypeError, Uint8Array, - Uint8ArrayPrototype, } = primordials; const state = Symbol("[[state]]"); @@ -119,7 +120,8 @@ class FileReader extends EventTarget { // and whose value property is a Uint8Array object, run these steps: if ( !chunk.done && - ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk.value) + TypedArrayPrototypeGetSymbolToStringTag(chunk.value) === + "Uint8Array" ) { ArrayPrototypePush(chunks, chunk.value); @@ -127,7 +129,7 @@ class FileReader extends EventTarget { { const size = ArrayPrototypeReduce( chunks, - (p, i) => p + i.byteLength, + (p, i) => p + TypedArrayPrototypeGetByteLength(i), 0, ); const ev = new ProgressEvent("progress", { @@ -151,7 +153,7 @@ class FileReader extends EventTarget { // 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, + (p, i) => p + TypedArrayPrototypeGetByteLength(i), 0, ); const bytes = new Uint8Array(size); @@ -159,11 +161,11 @@ class FileReader extends EventTarget { for (let i = 0; i < chunks.length; ++i) { const chunk = chunks[i]; TypedArrayPrototypeSet(bytes, chunk, offs); - offs += chunk.byteLength; + offs += TypedArrayPrototypeGetByteLength(chunk); } switch (readtype.kind) { case "ArrayBuffer": { - this[result] = bytes.buffer; + this[result] = TypedArrayPrototypeGetBuffer(bytes); break; } case "BinaryString": diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index f50d14d1ae..6227bf92bc 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -19,6 +19,7 @@ import DOMException from "ext:deno_web/01_dom_exception.js"; const primordials = globalThis.__bootstrap.primordials; const { ArrayBufferPrototype, + ArrayBufferPrototypeGetByteLength, ArrayPrototypeFilter, ArrayPrototypeIncludes, ArrayPrototypePush, @@ -249,7 +250,10 @@ function serializeJsMessageData(data, transferables) { 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)) { + if ( + ArrayBufferPrototypeGetByteLength(ab) === 0 && + ops.op_arraybuffer_was_detached(ab) + ) { throw new DOMException( `ArrayBuffer at index ${j} is already detached`, "DataCloneError", diff --git a/ext/web/14_compression.js b/ext/web/14_compression.js index a3bca50e55..f6f22bed38 100644 --- a/ext/web/14_compression.js +++ b/ext/web/14_compression.js @@ -7,6 +7,10 @@ const core = globalThis.Deno.core; const ops = core.ops; +const primordials = globalThis.__bootstrap.primordials; +const { + TypedArrayPrototypeGetByteLength, +} = primordials; import * as webidl from "ext:deno_webidl/00_webidl.js"; import { TransformStream } from "ext:deno_web/06_streams.js"; @@ -113,7 +117,7 @@ class DecompressionStream { } function maybeEnqueue(controller, output) { - if (output && output.byteLength > 0) { + if (output && TypedArrayPrototypeGetByteLength(output) > 0) { controller.enqueue(output); } } diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js index 124c81c73a..43c0cb6218 100644 --- a/ext/webidl/00_webidl.js +++ b/ext/webidl/00_webidl.js @@ -18,6 +18,7 @@ const { BigInt, BigIntAsIntN, BigIntAsUintN, + DataViewPrototypeGetBuffer, Float32Array, Float64Array, FunctionPrototypeBind, @@ -76,6 +77,7 @@ const { Symbol, SymbolIterator, SymbolToStringTag, + TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetSymbolToStringTag, TypeError, Uint16Array, @@ -476,7 +478,7 @@ converters.DataView = (V, opts = {}) => { throw makeException(TypeError, "is not a DataView", opts); } - if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) { + if (!opts.allowShared && isSharedArrayBuffer(DataViewPrototypeGetBuffer(V))) { throw makeException( TypeError, "is backed by a SharedArrayBuffer, which is not allowed", @@ -512,7 +514,10 @@ ArrayPrototypeForEach( opts, ); } - if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) { + if ( + !opts.allowShared && + isSharedArrayBuffer(TypedArrayPrototypeGetBuffer(V)) + ) { throw makeException( TypeError, "is a view on a SharedArrayBuffer, which is not allowed", @@ -535,8 +540,13 @@ converters.ArrayBufferView = (V, opts = {}) => { opts, ); } - - if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) { + let buffer; + if (TypedArrayPrototypeGetSymbolToStringTag(V) !== undefined) { + buffer = TypedArrayPrototypeGetBuffer(V); + } else { + buffer = DataViewPrototypeGetBuffer(V); + } + if (!opts.allowShared && isSharedArrayBuffer(buffer)) { throw makeException( TypeError, "is a view on a SharedArrayBuffer, which is not allowed", @@ -549,7 +559,13 @@ converters.ArrayBufferView = (V, opts = {}) => { converters.BufferSource = (V, opts = {}) => { if (ArrayBufferIsView(V)) { - if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) { + let buffer; + if (TypedArrayPrototypeGetSymbolToStringTag(V) !== undefined) { + buffer = TypedArrayPrototypeGetBuffer(V); + } else { + buffer = DataViewPrototypeGetBuffer(V); + } + if (!opts.allowShared && isSharedArrayBuffer(buffer)) { throw makeException( TypeError, "is a view on a SharedArrayBuffer, which is not allowed", diff --git a/ext/websocket/01_websocket.js b/ext/websocket/01_websocket.js index cadd35a50e..5105df24d7 100644 --- a/ext/websocket/01_websocket.js +++ b/ext/websocket/01_websocket.js @@ -22,16 +22,19 @@ const primordials = globalThis.__bootstrap.primordials; const { ArrayBufferPrototype, ArrayBufferIsView, + ArrayBufferPrototypeGetByteLength, ArrayPrototypeJoin, ArrayPrototypeMap, ArrayPrototypeSome, DataView, + DataViewPrototypeGetByteLength, ErrorPrototypeToString, ObjectDefineProperties, ObjectPrototypeIsPrototypeOf, PromisePrototypeThen, RegExpPrototypeTest, Set, + SetPrototypeGetSize, // TODO(lucacasonato): add SharedArrayBuffer to primordials // SharedArrayBufferPrototype String, @@ -41,6 +44,8 @@ const { SymbolIterator, PromisePrototypeCatch, SymbolFor, + TypedArrayPrototypeGetByteLength, + TypedArrayPrototypeGetSymbolToStringTag, } = primordials; webidl.converters["sequence or DOMString"] = (V, opts) => { @@ -211,9 +216,11 @@ class WebSocket extends EventTarget { if ( protocols.length !== - new Set( - ArrayPrototypeMap(protocols, (p) => StringPrototypeToLowerCase(p)), - ).size + SetPrototypeGetSize( + new Set( + ArrayPrototypeMap(protocols, (p) => StringPrototypeToLowerCase(p)), + ), + ) ) { throw new DOMException( "Can't supply multiple times the same protocol.", @@ -298,12 +305,16 @@ class WebSocket extends EventTarget { throw new DOMException("readyState not OPEN", "InvalidStateError"); } - const sendTypedArray = (ta) => { - this[_bufferedAmount] += ta.byteLength; + /** + * @param {ArrayBufferView} view + * @param {number} byteLength + */ + const sendTypedArray = (view, byteLength) => { + this[_bufferedAmount] += byteLength; PromisePrototypeThen( - core.opAsync2("op_ws_send_binary", this[_rid], ta), + core.opAsync2("op_ws_send_binary", this[_rid], view), () => { - this[_bufferedAmount] -= ta.byteLength; + this[_bufferedAmount] -= byteLength; }, ); }; @@ -311,20 +322,33 @@ class WebSocket extends EventTarget { if (ObjectPrototypeIsPrototypeOf(BlobPrototype, data)) { PromisePrototypeThen( data.slice().arrayBuffer(), - (ab) => sendTypedArray(new DataView(ab)), + (ab) => + sendTypedArray( + new DataView(ab), + ArrayBufferPrototypeGetByteLength(ab), + ), ); } else if (ArrayBufferIsView(data)) { - sendTypedArray(data); + if (TypedArrayPrototypeGetSymbolToStringTag(data) === undefined) { + // DataView + sendTypedArray(data, DataViewPrototypeGetByteLength(data)); + } else { + // TypedArray + sendTypedArray(data, TypedArrayPrototypeGetByteLength(data)); + } } else if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, data)) { - sendTypedArray(new DataView(data)); + sendTypedArray( + new DataView(data), + ArrayBufferPrototypeGetByteLength(data), + ); } else { const string = String(data); const d = core.encode(string); - this[_bufferedAmount] += d.byteLength; + this[_bufferedAmount] += TypedArrayPrototypeGetByteLength(d); PromisePrototypeThen( core.opAsync2("op_ws_send_text", this[_rid], string), () => { - this[_bufferedAmount] -= d.byteLength; + this[_bufferedAmount] -= TypedArrayPrototypeGetByteLength(d); }, ); } @@ -361,7 +385,10 @@ class WebSocket extends EventTarget { } } - if (reason !== undefined && core.encode(reason).byteLength > 123) { + if ( + reason !== undefined && + TypedArrayPrototypeGetByteLength(core.encode(reason)) > 123 + ) { throw new DOMException( "The close reason may not be longer than 123 bytes.", "SyntaxError", diff --git a/ext/websocket/02_websocketstream.js b/ext/websocket/02_websocketstream.js index 6e487f0b7c..46bd7deae1 100644 --- a/ext/websocket/02_websocketstream.js +++ b/ext/websocket/02_websocketstream.js @@ -22,10 +22,12 @@ const { PromisePrototypeCatch, PromisePrototypeThen, Set, + SetPrototypeGetSize, StringPrototypeEndsWith, StringPrototypeToLowerCase, Symbol, SymbolFor, + TypedArrayPrototypeGetByteLength, TypeError, Uint8ArrayPrototype, } = primordials; @@ -115,12 +117,14 @@ class WebSocketStream { if ( options.protocols.length !== - new Set( - ArrayPrototypeMap( - options.protocols, - (p) => StringPrototypeToLowerCase(p), + SetPrototypeGetSize( + new Set( + ArrayPrototypeMap( + options.protocols, + (p) => StringPrototypeToLowerCase(p), + ), ), - ).size + ) ) { throw new DOMException( "Can't supply multiple times the same protocol.", @@ -394,7 +398,8 @@ class WebSocketStream { const encoder = new TextEncoder(); if ( - closeInfo.reason && encoder.encode(closeInfo.reason).byteLength > 123 + closeInfo.reason && + TypedArrayPrototypeGetByteLength(encoder.encode(closeInfo.reason)) > 123 ) { throw new DOMException( "The close reason may not be longer than 123 bytes.", diff --git a/runtime/js/13_buffer.js b/runtime/js/13_buffer.js index 4534152f3e..907b07128d 100644 --- a/runtime/js/13_buffer.js +++ b/runtime/js/13_buffer.js @@ -7,9 +7,12 @@ import { assert } from "ext:deno_web/00_infra.js"; const primordials = globalThis.__bootstrap.primordials; const { + ArrayBufferPrototypeGetByteLength, TypedArrayPrototypeSubarray, TypedArrayPrototypeSlice, TypedArrayPrototypeSet, + TypedArrayPrototypeGetBuffer, + TypedArrayPrototypeGetByteLength, MathFloor, MathMin, PromiseResolve, @@ -28,12 +31,12 @@ const MAX_SIZE = 2 ** 32 - 2; // from `src`. // Returns the number of bytes copied. function copyBytes(src, dst, off = 0) { - const r = dst.byteLength - off; - if (src.byteLength > r) { + const r = TypedArrayPrototypeGetByteLength(dst) - off; + if (TypedArrayPrototypeGetByteLength(src) > r) { src = TypedArrayPrototypeSubarray(src, 0, r); } TypedArrayPrototypeSet(dst, src, off); - return src.byteLength; + return TypedArrayPrototypeGetByteLength(src); } class Buffer { @@ -57,15 +60,17 @@ class Buffer { } empty() { - return this.#buf.byteLength <= this.#off; + return TypedArrayPrototypeGetByteLength(this.#buf) <= this.#off; } get length() { - return this.#buf.byteLength - this.#off; + return TypedArrayPrototypeGetByteLength(this.#buf) - this.#off; } get capacity() { - return this.#buf.buffer.byteLength; + return ArrayBufferPrototypeGetByteLength( + TypedArrayPrototypeGetBuffer(this.#buf), + ); } truncate(n) { @@ -85,7 +90,7 @@ class Buffer { } #tryGrowByReslice(n) { - const l = this.#buf.byteLength; + const l = TypedArrayPrototypeGetByteLength(this.#buf); if (n <= this.capacity - l) { this.#reslice(l + n); return l; @@ -94,15 +99,16 @@ class Buffer { } #reslice(len) { - assert(len <= this.#buf.buffer.byteLength); - this.#buf = new Uint8Array(this.#buf.buffer, 0, len); + const ab = TypedArrayPrototypeGetBuffer(this.#buf); + assert(len <= ArrayBufferPrototypeGetByteLength(ab)); + this.#buf = new Uint8Array(ab, 0, len); } readSync(p) { if (this.empty()) { // Buffer is empty, reset to recover space. this.reset(); - if (p.byteLength === 0) { + if (TypedArrayPrototypeGetByteLength(p) === 0) { // this edge case is tested in 'bufferReadEmptyAtEOF' test return 0; } @@ -122,7 +128,7 @@ class Buffer { } writeSync(p) { - const m = this.#grow(p.byteLength); + const m = this.#grow(TypedArrayPrototypeGetByteLength(p)); return copyBytes(p, this.#buf, m); } @@ -180,7 +186,7 @@ class Buffer { // otherwise read directly into the internal buffer const buf = shouldGrow ? tmp - : new Uint8Array(this.#buf.buffer, this.length); + : new Uint8Array(TypedArrayPrototypeGetBuffer(this.#buf), this.length); const nread = await r.read(buf); if (nread === null) { @@ -205,7 +211,7 @@ class Buffer { // otherwise read directly into the internal buffer const buf = shouldGrow ? tmp - : new Uint8Array(this.#buf.buffer, this.length); + : new Uint8Array(TypedArrayPrototypeGetBuffer(this.#buf), this.length); const nread = r.readSync(buf); if (nread === null) { diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 956f35d0aa..fa16cc1f40 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -411,25 +411,25 @@ function bootstrapMainRuntime(runtimeOptions) { throw new Error("Worker runtime already bootstrapped"); } - const [ - args, - cpuCount, - debugFlag, - denoVersion, - locale, - location_, - noColor, - isTty, - tsVersion, - unstableFlag, - pid, - ppid, - target, - v8Version, - userAgent, - inspectFlag, - _, - ] = runtimeOptions; + const { + 0: args, + 1: cpuCount, + 2: debugFlag, + 3: denoVersion, + 4: locale, + 5: location_, + 6: noColor, + 7: isTty, + 8: tsVersion, + 9: unstableFlag, + 10: pid, + 11: ppid, + 12: target, + 13: v8Version, + 14: userAgent, + 15: inspectFlag, + // 16: enableTestingFeaturesFlag + } = runtimeOptions; performance.setTimeOrigin(DateNow()); globalThis_ = globalThis; @@ -519,25 +519,25 @@ function bootstrapWorkerRuntime( throw new Error("Worker runtime already bootstrapped"); } - const [ - args, - cpuCount, - debugFlag, - denoVersion, - locale, - location_, - noColor, - isTty, - tsVersion, - unstableFlag, - pid, - _ppid, - target, - v8Version, - _userAgent, - _inspectFlag, - enableTestingFeaturesFlag, - ] = runtimeOptions; + const { + 0: args, + 1: cpuCount, + 2: debugFlag, + 3: denoVersion, + 4: locale, + 5: location_, + 6: noColor, + 7: isTty, + 8: tsVersion, + 9: unstableFlag, + 10: pid, + // 11: ppid, + 12: target, + 13: v8Version, + // 14: userAgent, + // 15: inspectFlag, + 16: enableTestingFeaturesFlag, + } = runtimeOptions; performance.setTimeOrigin(DateNow()); globalThis_ = globalThis; diff --git a/tools/lint.js b/tools/lint.js index f77ddbaf7d..bdaa015621 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -25,8 +25,7 @@ if (Deno.args.includes("--rs")) { if (!didLint) { await Promise.all([ dlint(), - // todo(dsherret): re-enable - // dlintPreferPrimordials(), + dlintPreferPrimordials(), checkCopyright(), clippy(), ]); @@ -96,6 +95,10 @@ async function dlintPreferPrimordials() { const sourceFiles = await getSources(ROOT_PATH, [ "runtime/**/*.js", "ext/**/*.js", + // TODO(petamoriken): enable for node polyfills + // "ext/node/polyfills/*.mjs", + // "ext/node/polyfills/*.ts", + // ":!:ext/node/polyfills/*.d.ts", "core/*.js", ":!:core/*_test.js", ":!:core/examples/**",