From ee07ef2962e5fe52e5f40b7516cdd813109e6104 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 8 Apr 2021 01:23:26 +0200 Subject: [PATCH] chore: update deno_file to use deno_webidl (#10042) This changes the custom input converters in deno_file to use deno_webidl converters. --- op_crates/file/01_file.js | 188 +++++++++++++++++++++++----------- op_crates/file/03_blob_url.js | 26 ++--- op_crates/webidl/00_webidl.js | 77 ++++++++------ test_util/std | 2 +- tools/wpt/expectation.json | 6 +- 5 files changed, 188 insertions(+), 111 deletions(-) diff --git a/op_crates/file/01_file.js b/op_crates/file/01_file.js index 23886fbd513500..17762c14a5b274 100644 --- a/op_crates/file/01_file.js +++ b/op_crates/file/01_file.js @@ -3,6 +3,7 @@ // @ts-check /// /// +/// /// /// /// @@ -11,6 +12,8 @@ "use strict"; ((window) => { + const webidl = window.__bootstrap.webidl; + // TODO(lucacasonato): this needs to not be hardcoded and instead depend on // host os. const isWindows = false; @@ -143,62 +146,39 @@ [_byteSequence]; /** - * @param {BlobPart[]} [blobParts] - * @param {BlobPropertyBag} [options] + * @param {BlobPart[]} blobParts + * @param {BlobPropertyBag} options */ - constructor(blobParts, options) { - if (blobParts === undefined) { - blobParts = []; - } - if (typeof blobParts !== "object") { - throw new TypeError( - `Failed to construct 'Blob'. blobParts cannot be converted to a sequence.`, - ); - } - - const parts = []; - const iterator = blobParts[Symbol.iterator]?.(); - if (iterator === undefined) { - throw new TypeError( - "Failed to construct 'Blob'. The provided value cannot be converted to a sequence", - ); - } - while (true) { - const { value: element, done } = iterator.next(); - if (done) break; - if ( - ArrayBuffer.isView(element) || element instanceof ArrayBuffer || - element instanceof Blob - ) { - parts.push(element); - } else { - parts.push(String(element)); - } - } + constructor(blobParts = [], options = {}) { + const prefix = "Failed to construct 'Blob'"; + blobParts = webidl.converters["sequence"](blobParts, { + context: "Argument 1", + prefix, + }); + options = webidl.converters["BlobPropertyBag"](options, { + context: "Argument 2", + prefix, + }); - if (!options || typeof options === "function") { - options = {}; - } - if (typeof options !== "object") { - throw new TypeError( - `Failed to construct 'Blob'. options is not an object.`, - ); - } - const endings = options.endings?.toString() ?? "transparent"; - const type = options.type?.toString() ?? ""; + this[webidl.brand] = webidl.brand; /** @type {Uint8Array} */ - this[_byteSequence] = processBlobParts(parts, endings); - this.#type = normalizeType(type); + this[_byteSequence] = processBlobParts( + blobParts, + options.endings, + ); + this.#type = normalizeType(options.type); } /** @returns {number} */ get size() { + webidl.assertBranded(this, Blob); return this[_byteSequence].byteLength; } /** @returns {string} */ get type() { + webidl.assertBranded(this, Blob); return this.#type; } @@ -209,13 +189,35 @@ * @returns {Blob} */ slice(start, end, contentType) { + webidl.assertBranded(this, Blob); + 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, + }); + } + const O = this; /** @type {number} */ let relativeStart; if (start === undefined) { relativeStart = 0; } else { - start = Number(start); if (start < 0) { relativeStart = Math.max(O.size + start, 0); } else { @@ -227,7 +229,6 @@ if (end === undefined) { relativeEnd = O.size; } else { - end = Number(end); if (end < 0) { relativeEnd = Math.max(O.size + end, 0); } else { @@ -239,7 +240,7 @@ if (contentType === undefined) { relativeContentType = ""; } else { - relativeContentType = normalizeType(String(contentType)); + relativeContentType = normalizeType(contentType); } return new Blob([ O[_byteSequence].buffer.slice(relativeStart, relativeEnd), @@ -250,6 +251,7 @@ * @returns {ReadableStream} */ stream() { + webidl.assertBranded(this, Blob); const bytes = this[_byteSequence]; const stream = new ReadableStream({ type: "bytes", @@ -267,6 +269,7 @@ * @returns {Promise} */ async text() { + webidl.assertBranded(this, Blob); const buffer = await this.arrayBuffer(); return utf8Decoder.decode(buffer); } @@ -275,6 +278,7 @@ * @returns {Promise} */ async arrayBuffer() { + webidl.assertBranded(this, Blob); const stream = this.stream(); let bytes = new Uint8Array(); for await (const chunk of stream) { @@ -288,6 +292,46 @@ } } + webidl.converters["Blob"] = webidl.createInterfaceConverter("Blob", Blob); + webidl.converters["BlobPart"] = (V, opts) => { + // Union for ((ArrayBuffer or ArrayBufferView) or Blob or USVString) + if (typeof V == "object") { + if (V instanceof Blob) { + return webidl.converters["Blob"](V, opts); + } + if (V instanceof ArrayBuffer || V instanceof SharedArrayBuffer) { + return webidl.converters["ArrayBuffer"](V, opts); + } + if (ArrayBuffer.isView(V)) { + return webidl.converters["ArrayBufferView"](V, opts); + } + } + return webidl.converters["USVString"](V, opts); + }; + webidl.converters["sequence"] = 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 _LastModfied = Symbol("[[LastModified]]"); @@ -300,42 +344,62 @@ /** * @param {BlobPart[]} fileBits * @param {string} fileName - * @param {FilePropertyBag} [options] + * @param {FilePropertyBag} options */ - constructor(fileBits, fileName, options) { - if (fileBits === undefined) { - throw new TypeError( - "Failed to construct 'File'. 2 arguments required, but first not specified.", - ); - } - if (fileName === undefined) { - throw new TypeError( - "Failed to construct 'File'. 2 arguments required, but second not specified.", - ); - } - super(fileBits, { endings: options?.endings, type: options?.type }); + constructor(fileBits, fileName, options = {}) { + const prefix = "Failed to construct 'File'"; + webidl.requiredArguments(arguments.length, 2, { prefix }); + + fileBits = webidl.converters["sequence"](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); + /** @type {string} */ - this[_Name] = String(fileName).replaceAll("/", ":"); - if (options?.lastModified === undefined) { + this[_Name] = fileName.replaceAll("/", ":"); + if (options.lastModified === undefined) { /** @type {number} */ this[_LastModfied] = new Date().getTime(); } else { /** @type {number} */ - this[_LastModfied] = Number(options.lastModified); + this[_LastModfied] = options.lastModified; } } /** @returns {string} */ get name() { + webidl.assertBranded(this, File); return this[_Name]; } /** @returns {number} */ get lastModified() { + webidl.assertBranded(this, File); return this[_LastModfied]; } } + webidl.converters["FilePropertyBag"] = webidl.createDictionaryConverter( + "FilePropertyBag", + blobPropertyBagDictionary, + [ + { + key: "lastModified", + converter: webidl.converters["long long"], + }, + ], + ); + window.__bootstrap.file = { Blob, _byteSequence, diff --git a/op_crates/file/03_blob_url.js b/op_crates/file/03_blob_url.js index 29019cd843510c..a3ec904331dd60 100644 --- a/op_crates/file/03_blob_url.js +++ b/op_crates/file/03_blob_url.js @@ -15,7 +15,7 @@ ((window) => { const core = Deno.core; - // const webidl = window.__bootstrap.webidl; + const webidl = window.__bootstrap.webidl; const { _byteSequence } = window.__bootstrap.file; const { URL } = window.__bootstrap.url; @@ -24,12 +24,12 @@ * @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 prefix = "Failed to execute 'createObjectURL' on 'URL'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + blob = webidl.converters["Blob"](blob, { + context: "Argument 1", + prefix, + }); const url = core.jsonOpSync( "op_file_create_object_url", @@ -45,12 +45,12 @@ * @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, - // }); + const prefix = "Failed to execute 'revokeObjectURL' on 'URL'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + url = webidl.converters["DOMString"](url, { + context: "Argument 1", + prefix, + }); core.jsonOpSync( "op_file_revoke_object_url", diff --git a/op_crates/webidl/00_webidl.js b/op_crates/webidl/00_webidl.js index c00c605e88d6f0..843fd329e6a171 100644 --- a/op_crates/webidl/00_webidl.js +++ b/op_crates/webidl/00_webidl.js @@ -615,6 +615,19 @@ } function createDictionaryConverter(name, ...dictionaries) { + const allMembers = []; + for (const members of dictionaries) { + for (const member of members) { + allMembers.push(member); + } + } + allMembers.sort((a, b) => { + if (a.key == b.key) { + return 0; + } + return a.key < b.key ? -1 : 1; + }); + return function (V, opts = {}) { const typeV = type(V); switch (typeV) { @@ -633,39 +646,37 @@ const idlDict = {}; - for (const members of dictionaries) { - for (const member of members) { - const key = member.key; - - let esMemberValue; - if (typeV === "Undefined" || typeV === "Null") { - esMemberValue = undefined; - } else { - esMemberValue = esDict[key]; - } - - const context = `'${key}' of '${name}'${ - opts.context ? ` (${opts.context})` : "" - }`; - - if (esMemberValue !== undefined) { - const converter = member.converter; - const idlMemberValue = converter(esMemberValue, { - ...opts, - context, - }); - idlDict[key] = idlMemberValue; - } else if ("defaultValue" in member) { - const defaultValue = member.defaultValue; - const idlMemberValue = defaultValue; - idlDict[key] = idlMemberValue; - } else if (member.required) { - throw makeException( - TypeError, - `can not be converted to '${name}' because '${key}' is required in '${name}'.`, - { ...opts }, - ); - } + for (const member of allMembers) { + const key = member.key; + + let esMemberValue; + if (typeV === "Undefined" || typeV === "Null") { + esMemberValue = undefined; + } else { + esMemberValue = esDict[key]; + } + + const context = `'${key}' of '${name}'${ + opts.context ? ` (${opts.context})` : "" + }`; + + if (esMemberValue !== undefined) { + const converter = member.converter; + const idlMemberValue = converter(esMemberValue, { + ...opts, + context, + }); + idlDict[key] = idlMemberValue; + } else if ("defaultValue" in member) { + const defaultValue = member.defaultValue; + const idlMemberValue = defaultValue; + idlDict[key] = idlMemberValue; + } else if (member.required) { + throw makeException( + TypeError, + `can not be converted to '${name}' because '${key}' is required in '${name}'.`, + { ...opts }, + ); } } diff --git a/test_util/std b/test_util/std index 98398fa7f43aa9..8467929052737f 160000 --- a/test_util/std +++ b/test_util/std @@ -1 +1 @@ -Subproject commit 98398fa7f43aa9d15b4a5757c248a1dd21e59bdb +Subproject commit 8467929052737f634eb8842a960093c686ae3b9b diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index 2ebdc101145207..4f04213aa30c06 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -776,7 +776,6 @@ "Blob-stream.any.js": true, "Blob-text.any.js": true, "Blob-constructor.any.js": [ - "Blob interface object", "Passing a FrozenArray as the blobParts array should work (FrozenArray)." ], "Blob-slice-overflow.any.js": true, @@ -785,7 +784,10 @@ "file": { "File-constructor.any.js": true }, - "fileReader.any.js": true + "fileReader.any.js": true, + "url": { + "url-format.any.js": true + } }, "html": { "webappapis": {