wasm-bindgen-webidl-0.2.75/.cargo_vcs_info.json0000644000000001120000000000100147400ustar { "git": { "sha1": "e104d1695a89de8c8050b7abaedf5ea9330f3cd8" } } wasm-bindgen-webidl-0.2.75/Cargo.lock0000644000000230740000000000100127270ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ "winapi", ] [[package]] name = "anyhow" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bumpalo" version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", "bitflags", "strsim", "textwrap", "unicode-width", "vec_map", ] [[package]] name = "env_logger" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "atty", "humantime", "log", "regex", "termcolor", ] [[package]] name = "heck" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "nom" version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "memchr", "version_check", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "sourcefile" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69b041cdcb67226aca307e6e7be44c8806423d83e018bd662360a93dabce4d71" dependencies = [ "clap", "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7813934aecf5f51a54775e00068c237de98489463968231a51746bbbc03f9c10" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "termcolor" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "unicode-segmentation" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "wasm-bindgen-backend" version = "0.2.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f" dependencies = [ "bumpalo", "lazy_static", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2" [[package]] name = "wasm-bindgen-webidl" version = "0.2.75" dependencies = [ "anyhow", "env_logger", "heck", "lazy_static", "log", "proc-macro2", "quote", "sourcefile", "structopt", "syn", "wasm-bindgen-backend", "weedle", ] [[package]] name = "weedle" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "610950904727748ca09682e857f0d6d6437f0ca862f32f9229edba8cec8b2635" dependencies = [ "nom", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" wasm-bindgen-webidl-0.2.75/Cargo.toml0000644000000026760000000000100127570ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "wasm-bindgen-webidl" version = "0.2.75" authors = ["The wasm-bindgen Developers"] description = "Support for parsing WebIDL specific to wasm-bindgen\n" homepage = "https://rustwasm.github.io/wasm-bindgen/" documentation = "https://docs.rs/wasm-bindgen" categories = ["wasm"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/webidl" [dependencies.anyhow] version = "1.0" [dependencies.env_logger] version = "0.8.1" [dependencies.heck] version = "0.3" [dependencies.lazy_static] version = "1.0.2" [dependencies.log] version = "0.4.1" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.sourcefile] version = "0.1" [dependencies.structopt] version = "0.3.9" [dependencies.syn] version = "1.0" features = ["full"] [dependencies.wasm-bindgen-backend] version = "=0.2.75" [dependencies.weedle] version = "0.12" wasm-bindgen-webidl-0.2.75/Cargo.toml.orig000064400000000000000000000013300072674642500164520ustar 00000000000000[package] name = "wasm-bindgen-webidl" version = "0.2.75" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" categories = ["wasm"] repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/webidl" homepage = "https://rustwasm.github.io/wasm-bindgen/" documentation = "https://docs.rs/wasm-bindgen" description = """ Support for parsing WebIDL specific to wasm-bindgen """ edition = "2018" [dependencies] env_logger = "0.8.1" anyhow = "1.0" heck = "0.3" log = "0.4.1" proc-macro2 = "1.0" quote = '1.0' syn = { version = '1.0', features = ['full'] } wasm-bindgen-backend = { version = "=0.2.75", path = "../backend" } weedle = "0.12" lazy_static = "1.0.2" sourcefile = "0.1" structopt = "0.3.9" wasm-bindgen-webidl-0.2.75/LICENSE-APACHE000064400000000000000000000251370072674642500155220ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. wasm-bindgen-webidl-0.2.75/LICENSE-MIT000064400000000000000000000020410072674642500152170ustar 00000000000000Copyright (c) 2014 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wasm-bindgen-webidl-0.2.75/src/constants.rs000064400000000000000000000044140072674642500167420ustar 00000000000000use lazy_static::lazy_static; use std::collections::BTreeSet; use std::iter::FromIterator; lazy_static! { pub(crate) static ref BUILTIN_IDENTS: BTreeSet<&'static str> = BTreeSet::from_iter(vec![ "str", "char", "bool", "JsValue", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize", "isize", "f32", "f64", "Result", "String", "Vec", "Option", "Array", "ArrayBuffer", "Object", "Promise", "Function", "Clamped", "DataView", ]); // whitelist a few names that have known polyfills pub(crate) static ref POLYFILL_INTERFACES: BTreeSet<&'static str> = BTreeSet::from_iter(vec![ "AudioContext", "OfflineAudioContext", ]); pub(crate) static ref IMMUTABLE_SLICE_WHITELIST: BTreeSet<&'static str> = BTreeSet::from_iter(vec![ // ImageData "ImageData", // WebGlRenderingContext, WebGl2RenderingContext "uniform1fv", "uniform2fv", "uniform3fv", "uniform4fv", "uniform1iv", "uniform2iv", "uniform3iv", "uniform4iv", "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv", "uniformMatrix2x3fv", "uniformMatrix2x4fv", "uniformMatrix3x2fv", "uniformMatrix3x4fv", "uniformMatrix4x2fv", "uniformMatrix4x3fv", "vertexAttrib1fv", "vertexAttrib2fv", "vertexAttrib3fv", "vertexAttrib4fv", "bufferData", "bufferSubData", "texImage2D", "texSubImage2D", "compressedTexImage2D", // WebGl2RenderingContext "uniform1uiv", "uniform2uiv", "uniform3uiv", "uniform4uiv", "texImage3D", "texSubImage3D", "compressedTexImage3D", "clearBufferfv", "clearBufferiv", "clearBufferuiv", // WebSocket "send", // WebGPU "setBindGroup", "writeBuffer", "writeTexture", // AudioBuffer "copyToChannel" // TODO: Add another type's functions here. Leave a comment header with the type name ]); } wasm-bindgen-webidl-0.2.75/src/first_pass.rs000064400000000000000000000667150072674642500171170ustar 00000000000000//! Because some WebIDL constructs are defined in multiple places //! (keyword `partial` is used to add to an existing construct), //! We need to first walk the webidl to collect all non-partial //! constructs so that we have containers in which to put the //! partial ones. //! //! Only `interface`s, `dictionary`s, `enum`s and `mixin`s can //! be partial. use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet}; use weedle; use weedle::argument::Argument; use weedle::attribute::*; use weedle::interface::*; use weedle::mixin::*; use weedle::CallbackInterfaceDefinition; use weedle::{DictionaryDefinition, PartialDictionaryDefinition}; use super::Result; use crate::{ util::{self, camel_case_ident}, ApiStability, }; /// Collection of constructs that may use partial. #[derive(Default)] pub(crate) struct FirstPassRecord<'src> { pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>, pub(crate) enums: BTreeMap<&'src str, EnumData<'src>>, /// The mixins, mapping their name to the webidl ast node for the mixin. pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>, pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>, pub(crate) namespaces: BTreeMap<&'src str, NamespaceData<'src>>, pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>, pub(crate) dictionaries: BTreeMap<&'src str, DictionaryData<'src>>, pub(crate) callbacks: BTreeSet<&'src str>, pub(crate) callback_interfaces: BTreeMap<&'src str, CallbackInterfaceData<'src>>, } pub(crate) struct AttributeInterfaceData<'src> { pub(crate) definition: &'src AttributeInterfaceMember<'src>, pub(crate) stability: ApiStability, } /// We need to collect interface data during the first pass, to be used later. #[derive(Default)] pub(crate) struct InterfaceData<'src> { /// Whether only partial interfaces were encountered pub(crate) partial: bool, pub(crate) has_interface: bool, pub(crate) deprecated: Option, pub(crate) attributes: Vec>, pub(crate) consts: Vec<&'src ConstMember<'src>>, pub(crate) operations: BTreeMap, OperationData<'src>>, pub(crate) superclass: Option<&'src str>, pub(crate) definition_attributes: Option<&'src ExtendedAttributeList<'src>>, pub(crate) stability: ApiStability, } pub(crate) struct AttributeMixinData<'src> { pub(crate) definition: &'src AttributeMixinMember<'src>, pub(crate) stability: ApiStability, } /// We need to collect mixin data during the first pass, to be used later. #[derive(Default)] pub(crate) struct MixinData<'src> { /// Whether only partial mixins were encountered pub(crate) partial: bool, pub(crate) attributes: Vec>, pub(crate) consts: Vec<&'src ConstMember<'src>>, pub(crate) operations: BTreeMap, OperationData<'src>>, pub(crate) definition_attributes: Option<&'src ExtendedAttributeList<'src>>, pub(crate) stability: ApiStability, } /// We need to collect namespace data during the first pass, to be used later. #[derive(Default)] pub(crate) struct NamespaceData<'src> { pub(crate) operations: BTreeMap, OperationData<'src>>, } #[derive(Default)] pub(crate) struct DictionaryData<'src> { pub(crate) partials: Vec<&'src PartialDictionaryDefinition<'src>>, pub(crate) definition: Option<&'src DictionaryDefinition<'src>>, pub(crate) stability: ApiStability, } pub(crate) struct EnumData<'src> { pub(crate) definition: &'src weedle::EnumDefinition<'src>, pub(crate) stability: ApiStability, } pub(crate) struct CallbackInterfaceData<'src> { pub(crate) definition: &'src CallbackInterfaceDefinition<'src>, pub(crate) single_function: bool, } #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] pub(crate) enum OperationId<'src> { /// The name of a constructor in crates/web-sys/webidls/enabled/*.webidl /// /// ex: Constructor(Some("ImageData")) Constructor(Option<&'src str>), NamedConstructor(IgnoreTraits<&'src str>), /// The name of a function in crates/web-sys/webidls/enabled/*.webidl /// /// ex: Operation(Some("vertexAttrib1fv")) Operation(Option<&'src str>), IndexingGetter, IndexingSetter, IndexingDeleter, } #[derive(Default)] pub(crate) struct OperationData<'src> { pub(crate) signatures: Vec>, pub(crate) is_static: bool, } #[derive(Clone, Debug)] pub(crate) struct Signature<'src> { pub(crate) args: Vec>, pub(crate) ret: weedle::types::ReturnType<'src>, pub(crate) attrs: &'src Option>, } #[derive(Clone, Debug)] pub(crate) struct Arg<'src> { pub(crate) name: &'src str, pub(crate) ty: &'src weedle::types::Type<'src>, pub(crate) optional: bool, pub(crate) variadic: bool, } /// Implemented on an AST node to populate the `FirstPassRecord` struct. pub(crate) trait FirstPass<'src, Ctx> { /// Populate `record` with any constructs in `self`. fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, ctx: Ctx) -> Result<()>; } impl<'src> FirstPass<'src, ApiStability> for [weedle::Definition<'src>] { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { for def in self { def.first_pass(record, stability)?; } Ok(()) } } impl<'src> FirstPass<'src, ApiStability> for weedle::Definition<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { use weedle::Definition::*; match self { Dictionary(dictionary) => dictionary.first_pass(record, stability), PartialDictionary(dictionary) => dictionary.first_pass(record, ()), Enum(enum_) => enum_.first_pass(record, stability), IncludesStatement(includes) => includes.first_pass(record, ()), Interface(interface) => interface.first_pass(record, stability), PartialInterface(interface) => interface.first_pass(record, stability), InterfaceMixin(mixin) => mixin.first_pass(record, stability), PartialInterfaceMixin(mixin) => mixin.first_pass(record, stability), Namespace(namespace) => namespace.first_pass(record, ()), PartialNamespace(namespace) => namespace.first_pass(record, ()), Typedef(typedef) => typedef.first_pass(record, ()), Callback(callback) => callback.first_pass(record, ()), CallbackInterface(iface) => iface.first_pass(record, ()), Implements(_) => Ok(()), } } } impl<'src> FirstPass<'src, ApiStability> for weedle::DictionaryDefinition<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } let dictionary_data = record.dictionaries.entry(self.identifier.0).or_default(); dictionary_data.definition = Some(self); dictionary_data.stability = stability; Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::PartialDictionaryDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record .dictionaries .entry(self.identifier.0) .or_default() .partials .push(self); Ok(()) } } impl<'src> FirstPass<'src, ApiStability> for weedle::EnumDefinition<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } let enum_data = EnumData { definition: self, stability, }; if record.enums.insert(self.identifier.0, enum_data).is_some() { log::info!( "Encountered multiple enum declarations: {}", self.identifier.0 ); } Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::IncludesStatementDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record .includes .entry(self.lhs_identifier.0) .or_default() .insert(self.rhs_identifier.0); Ok(()) } } #[derive(Clone, Copy)] enum FirstPassOperationType { Interface, Mixin, Namespace, } fn first_pass_operation<'src>( record: &mut FirstPassRecord<'src>, first_pass_operation_type: FirstPassOperationType, self_name: &'src str, ids: &[OperationId<'src>], arguments: &'src [Argument<'src>], ret: &weedle::types::ReturnType<'src>, attrs: &'src Option>, is_static: bool, ) { if util::is_chrome_only(attrs) { return; } let mut names = Vec::with_capacity(arguments.len()); for argument in arguments { match argument { Argument::Single(single) => names.push(single.identifier.0), Argument::Variadic(variadic) => names.push(variadic.identifier.0), } } let operations = match first_pass_operation_type { FirstPassOperationType::Interface => { let x = record .interfaces .get_mut(self_name) .expect(&format!("not found {} interface", self_name)); &mut x.operations } FirstPassOperationType::Mixin => { let x = record .mixins .get_mut(self_name) .expect(&format!("not found {} mixin", self_name)); &mut x.operations } FirstPassOperationType::Namespace => { let x = record .namespaces .get_mut(self_name) .expect(&format!("not found {} namespace", self_name)); &mut x.operations } }; let mut args = Vec::with_capacity(arguments.len()); for argument in arguments { let (name, ty, optional, variadic) = match argument { Argument::Single(single) => ( single.identifier.0, &single.type_.type_, single.optional.is_some(), false, ), Argument::Variadic(variadic) => (variadic.identifier.0, &variadic.type_, false, true), }; args.push(Arg { name, ty, optional, variadic, }); } for id in ids { let op = operations.entry(*id).or_default(); op.is_static = is_static; op.signatures.push(Signature { args: args.clone(), ret: ret.clone(), attrs, }); } } impl<'src> FirstPass<'src, ApiStability> for weedle::InterfaceDefinition<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } let interface_data = record.interfaces.entry(self.identifier.0).or_default(); interface_data.partial = false; interface_data.superclass = self.inheritance.map(|s| s.identifier.0); interface_data.definition_attributes = self.attributes.as_ref(); interface_data.deprecated = util::get_rust_deprecated(&self.attributes).map(|s| s.to_string()); interface_data.has_interface = !util::is_no_interface_object(&self.attributes); interface_data.stability = stability; if let Some(attrs) = &self.attributes { for attr in attrs.body.list.iter() { process_interface_attribute(record, self.identifier.0, attr); } } for member in &self.members.body { member.first_pass(record, (self.identifier.0, stability))?; } Ok(()) } } fn process_interface_attribute<'src>( record: &mut FirstPassRecord<'src>, self_name: &'src str, attr: &'src ExtendedAttribute<'src>, ) { let ident = weedle::common::Identifier(self_name); let non_null = weedle::types::MayBeNull { type_: ident, q_mark: None, }; let non_any = weedle::types::NonAnyType::Identifier(non_null); let single = weedle::types::SingleType::NonAny(non_any); let ty = weedle::types::Type::Single(single); let return_ty = weedle::types::ReturnType::Type(ty); match attr { ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { first_pass_operation( record, FirstPassOperationType::Interface, self_name, &[OperationId::Constructor(Some(self_name))], &list.args.body.list, &return_ty, &None, false, ); } ExtendedAttribute::NoArgs(other) if (other.0).0 == "Constructor" => { first_pass_operation( record, FirstPassOperationType::Interface, self_name, &[OperationId::Constructor(Some(self_name))], &[], &return_ty, &None, false, ); } ExtendedAttribute::NamedArgList(list) if list.lhs_identifier.0 == "NamedConstructor" => { first_pass_operation( record, FirstPassOperationType::Interface, self_name, &[OperationId::NamedConstructor(IgnoreTraits( list.rhs_identifier.0, ))], &list.args.body.list, &return_ty, &None, false, ); } _ => {} } } impl<'src> FirstPass<'src, ApiStability> for weedle::PartialInterfaceDefinition<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record .interfaces .entry(self.identifier.0) .or_insert_with(|| InterfaceData { partial: true, stability, ..Default::default() }); for member in &self.members.body { member.first_pass(record, (self.identifier.0, stability))?; } Ok(()) } } impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::interface::InterfaceMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, ctx: (&'src str, ApiStability), ) -> Result<()> { match self { InterfaceMember::Attribute(attr) => attr.first_pass(record, ctx), InterfaceMember::Operation(op) => op.first_pass(record, ctx.0), InterfaceMember::Const(const_) => { if util::is_chrome_only(&const_.attributes) { return Ok(()); } record .interfaces .get_mut(ctx.0) .unwrap() .consts .push(const_); Ok(()) } InterfaceMember::Constructor(_) => { log::warn!( "Unsupported WebIDL Constructor interface member: {:?}", self ); Ok(()) } InterfaceMember::Iterable(_iterable) => { log::warn!("Unsupported WebIDL iterable interface member: {:?}", self); Ok(()) } // TODO InterfaceMember::Maplike(_) => { log::warn!("Unsupported WebIDL Maplike interface member: {:?}", self); Ok(()) } InterfaceMember::Stringifier(_) => { log::warn!( "Unsupported WebIDL Stringifier interface member: {:?}", self ); Ok(()) } InterfaceMember::Setlike(_) => { log::warn!("Unsupported WebIDL Setlike interface member: {:?}", self); Ok(()) } InterfaceMember::AsyncIterable(_iterable) => { log::warn!( "Unsupported WebIDL async iterable interface member: {:?}", self ); Ok(()) } } } } impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str, ) -> Result<()> { let is_static = match self.modifier { Some(StringifierOrStatic::Stringifier(_)) => { log::warn!("Unsupported webidl stringifier: {:?}", self); return Ok(()); } Some(StringifierOrStatic::Static(_)) => true, None => false, }; let mut ids = vec![OperationId::Operation(self.identifier.map(|s| s.0))]; if let Some(special) = self.special { match special { Special::Getter(_) => ids.push(OperationId::IndexingGetter), Special::Setter(_) => ids.push(OperationId::IndexingSetter), Special::Deleter(_) => ids.push(OperationId::IndexingDeleter), Special::LegacyCaller(_) => {} }; } first_pass_operation( record, FirstPassOperationType::Interface, self_name, &ids, &self.args.body.list, &self.return_type, &self.attributes, is_static, ); Ok(()) } } impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::interface::AttributeInterfaceMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, ctx: (&'src str, ApiStability), ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record .interfaces .get_mut(ctx.0) .unwrap() .attributes .push(AttributeInterfaceData { definition: self, stability: ctx.1, }); Ok(()) } } impl<'src> FirstPass<'src, ApiStability> for weedle::InterfaceMixinDefinition<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } { let mixin_data = record.mixins.entry(self.identifier.0).or_default(); mixin_data.partial = false; mixin_data.definition_attributes = self.attributes.as_ref(); mixin_data.stability = stability; } for member in &self.members.body { member.first_pass(record, (self.identifier.0, stability))?; } Ok(()) } } impl<'src> FirstPass<'src, ApiStability> for weedle::PartialInterfaceMixinDefinition<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability, ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record .mixins .entry(self.identifier.0) .or_insert_with(|| MixinData { partial: true, stability, ..Default::default() }); for member in &self.members.body { member.first_pass(record, (self.identifier.0, stability))?; } Ok(()) } } impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::mixin::MixinMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, ctx: (&'src str, ApiStability), ) -> Result<()> { match self { MixinMember::Operation(op) => op.first_pass(record, ctx), MixinMember::Attribute(a) => a.first_pass(record, ctx), MixinMember::Const(a) => { if util::is_chrome_only(&a.attributes) { return Ok(()); } record.mixins.get_mut(ctx.0).unwrap().consts.push(a); Ok(()) } MixinMember::Stringifier(_) => { log::warn!("Unsupported WebIDL stringifier mixin member: {:?}", self); Ok(()) } } } } impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::mixin::OperationMixinMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, ctx: (&'src str, ApiStability), ) -> Result<()> { if self.stringifier.is_some() { log::warn!("Unsupported webidl stringifier: {:?}", self); return Ok(()); } first_pass_operation( record, FirstPassOperationType::Mixin, ctx.0, &[OperationId::Operation(self.identifier.map(|s| s.0.clone()))], &self.args.body.list, &self.return_type, &self.attributes, false, ); Ok(()) } } impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::mixin::AttributeMixinMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, ctx: (&'src str, ApiStability), ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record .mixins .get_mut(ctx.0) .unwrap() .attributes .push(AttributeMixinData { definition: self, stability: ctx.1, }); Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } if record .typedefs .insert(self.identifier.0, &self.type_.type_) .is_some() { log::info!( "Encountered multiple typedef declarations: {}", self.identifier.0 ); } Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record.namespaces.entry(self.identifier.0).or_default(); for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } record.namespaces.entry(self.identifier.0).or_default(); for member in &self.members.body { member.first_pass(record, self.identifier.0)?; } Ok(()) } } impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str, ) -> Result<()> { match self { weedle::namespace::NamespaceMember::Operation(op) => op.first_pass(record, self_name), _ => Ok(()), } } } impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { fn first_pass( &'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str, ) -> Result<()> { first_pass_operation( record, FirstPassOperationType::Namespace, self_name, &[OperationId::Operation(self.identifier.map(|s| s.0.clone()))], &self.args.body.list, &self.return_type, &self.attributes, true, ); Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::CallbackDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, _: ()) -> Result<()> { record.callbacks.insert(self.identifier.0); Ok(()) } } impl<'src> FirstPass<'src, ()> for weedle::CallbackInterfaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, _: ()) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } if self.inheritance.is_some() { log::warn!( "skipping callback interface with inheritance: {}", self.identifier.0 ); return Ok(()); } let data = CallbackInterfaceData { definition: self, single_function: self.members.body.len() == 1, }; record.callback_interfaces.insert(self.identifier.0, data); Ok(()) } } impl<'a> FirstPassRecord<'a> { pub fn all_superclasses<'me>(&'me self, interface: &str) -> impl Iterator + 'me { let mut set = BTreeSet::new(); let mut list = Vec::new(); self.fill_superclasses(interface, &mut set, &mut list); list.into_iter() } fn fill_superclasses( &self, interface: &str, set: &mut BTreeSet<&'a str>, list: &mut Vec, ) { let data = match self.interfaces.get(interface) { Some(data) => data, None => return, }; let superclass = match &data.superclass { Some(class) => class, None => return, }; if self.interfaces.contains_key(superclass) { if set.insert(superclass) { list.push(camel_case_ident(superclass)); self.fill_superclasses(superclass, set, list); } } } pub fn all_mixins<'me>( &'me self, interface: &str, ) -> impl Iterator> + 'me { let mut set = Vec::new(); self.fill_mixins(interface, interface, &mut set); set.into_iter() } fn fill_mixins<'me>( &'me self, self_name: &str, mixin_name: &str, list: &mut Vec<&'me MixinData<'a>>, ) { if let Some(mixin_data) = self.mixins.get(mixin_name) { list.push(mixin_data); } if let Some(mixin_names) = self.includes.get(mixin_name) { for mixin_name in mixin_names { self.fill_mixins(self_name, mixin_name, list); } } } } #[derive(Copy, Clone, Debug)] pub struct IgnoreTraits(pub T); impl PartialEq for IgnoreTraits { fn eq(&self, _other: &IgnoreTraits) -> bool { true } } impl Eq for IgnoreTraits {} impl PartialOrd for IgnoreTraits { fn partial_cmp(&self, _other: &IgnoreTraits) -> Option { Some(Ordering::Equal) } } impl Ord for IgnoreTraits { fn cmp(&self, _other: &IgnoreTraits) -> Ordering { Ordering::Equal } } wasm-bindgen-webidl-0.2.75/src/generator.rs000064400000000000000000000603240072674642500167160ustar 00000000000000use proc_macro2::Literal; use proc_macro2::TokenStream; use quote::quote; use std::collections::BTreeSet; use syn::{Ident, Type}; use wasm_bindgen_backend::util::{raw_ident, rust_ident}; use crate::constants::{BUILTIN_IDENTS, POLYFILL_INTERFACES}; use crate::traverse::TraverseType; use crate::util::{get_cfg_features, mdn_doc, required_doc_string, snake_case_ident}; use crate::Options; fn add_features(features: &mut BTreeSet, ty: &impl TraverseType) { ty.traverse_type(&mut |ident| { let ident = ident.to_string(); if !BUILTIN_IDENTS.contains(ident.as_str()) { features.insert(ident); } }); } fn get_features_doc(options: &Options, name: String) -> Option { let mut features = BTreeSet::new(); features.insert(name); required_doc_string(options, &features) } fn comment(mut comment: String, features: &Option) -> TokenStream { if let Some(s) = features { comment.push_str(s); } let lines = comment.lines().map(|doc| quote!( #[doc = #doc] )); quote! { #(#lines)* } } fn maybe_unstable_attr(unstable: bool) -> Option { if unstable { Some(quote! { #[cfg(web_sys_unstable_apis)] }) } else { None } } fn maybe_unstable_docs(unstable: bool) -> Option { if unstable { Some(quote! { #[doc = ""] #[doc = "*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as"] #[doc = "[described in the `wasm-bindgen` guide](https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html)*"] }) } else { None } } fn generate_arguments(arguments: &[(Ident, Type)], variadic: bool) -> Vec { arguments .into_iter() .enumerate() .map(|(i, (name, ty))| { if variadic && i + 1 == arguments.len() { quote!( #name: &::js_sys::Array ) } else { quote!( #name: #ty ) } }) .collect::>() } fn generate_variadic(variadic: bool) -> Option { if variadic { Some(quote!(variadic,)) } else { None } } pub struct EnumVariant { pub name: Ident, pub value: String, } impl EnumVariant { fn generate(&self) -> TokenStream { let EnumVariant { name, value } = self; quote!( #name = #value ) } } pub struct Enum { pub name: Ident, pub variants: Vec, pub unstable: bool, } impl Enum { pub fn generate(&self, options: &Options) -> TokenStream { let Enum { name, variants, unstable, } = self; let unstable_attr = maybe_unstable_attr(*unstable); let unstable_docs = maybe_unstable_docs(*unstable); let doc_comment = comment( format!("The `{}` enum.", name), &get_features_doc(options, name.to_string()), ); let variants = variants .into_iter() .map(|variant| variant.generate()) .collect::>(); quote! { #![allow(unused_imports)] use wasm_bindgen::prelude::*; #unstable_attr #[wasm_bindgen] #doc_comment #unstable_docs #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum #name { #(#variants),* } } } } pub enum InterfaceAttributeKind { Getter, Setter, } pub struct InterfaceAttribute { pub js_name: String, pub ty: Type, pub is_static: bool, pub structural: bool, pub catch: bool, pub kind: InterfaceAttributeKind, pub unstable: bool, } impl InterfaceAttribute { fn generate( &self, options: &Options, parent_name: &Ident, parent_js_name: &str, parents: &[Ident], ) -> TokenStream { let InterfaceAttribute { js_name, ty, is_static, structural, catch, kind, unstable, } = self; let unstable_attr = maybe_unstable_attr(*unstable); let unstable_docs = maybe_unstable_docs(*unstable); let mdn_docs = mdn_doc(parent_js_name, Some(js_name)); let mut features = BTreeSet::new(); add_features(&mut features, ty); for parent in parents { features.remove(&parent.to_string()); } features.remove(&parent_name.to_string()); let cfg_features = get_cfg_features(options, &features); features.insert(parent_name.to_string()); let doc_comment = required_doc_string(options, &features); let structural = if *structural { quote!(structural,) } else { quote!(final,) }; let (method, this) = if *is_static { (quote!( static_method_of = #parent_name, ), None) } else { (quote!(method,), Some(quote!( this: &#parent_name, ))) }; let (prefix, attr, def) = match kind { InterfaceAttributeKind::Getter => { let name = rust_ident(&snake_case_ident(js_name)); let ty = if *catch { quote!( Result<#ty, JsValue> ) } else { quote!( #ty ) }; ( "Getter", quote!(getter,), quote!( pub fn #name(#this) -> #ty; ), ) } InterfaceAttributeKind::Setter => { let name = rust_ident(&format!("set_{}", snake_case_ident(js_name))); let ret_ty = if *catch { Some(quote!( -> Result<(), JsValue> )) } else { None }; ( "Setter", quote!(setter,), quote!( pub fn #name(#this value: #ty) #ret_ty; ), ) } }; let catch = if *catch { Some(quote!(catch,)) } else { None }; let doc_comment = comment( format!( "{} for the `{}` field of this object.\n\n{}", prefix, js_name, mdn_docs ), &doc_comment, ); let js_name = raw_ident(js_name); quote! { #unstable_attr #cfg_features #[wasm_bindgen( #structural #catch #method #attr js_class = #parent_js_name, js_name = #js_name )] #doc_comment #unstable_docs #def } } } #[derive(Debug, Clone)] pub enum InterfaceMethodKind { Constructor(Option), Regular, IndexingGetter, IndexingSetter, IndexingDeleter, } pub struct InterfaceMethod { pub name: Ident, pub js_name: String, pub arguments: Vec<(Ident, Type)>, pub ret_ty: Option, pub kind: InterfaceMethodKind, pub is_static: bool, pub structural: bool, pub catch: bool, pub variadic: bool, pub unstable: bool, } impl InterfaceMethod { fn generate( &self, options: &Options, parent_name: &Ident, parent_js_name: String, parents: &[Ident], ) -> TokenStream { let InterfaceMethod { name, js_name, arguments, ret_ty, kind, is_static, structural, catch, variadic, unstable, } = self; let unstable_attr = maybe_unstable_attr(*unstable); let unstable_docs = maybe_unstable_docs(*unstable); let mut is_constructor = false; let mut extra_args = vec![quote!( js_class = #parent_js_name )]; let doc_comment = match kind { InterfaceMethodKind::Constructor(name) => { is_constructor = true; if let Some(name) = name { extra_args[0] = quote!( js_class = #name ); } format!( "The `new {}(..)` constructor, creating a new \ instance of `{0}`.\n\n{}", parent_name, mdn_doc(&parent_js_name, Some(&parent_js_name)) ) } InterfaceMethodKind::Regular => { { let js_name = raw_ident(js_name); extra_args.push(quote!( js_name = #js_name )); } format!( "The `{}()` method.\n\n{}", js_name, mdn_doc(&parent_js_name, Some(js_name)) ) } InterfaceMethodKind::IndexingGetter => { extra_args.push(quote!(indexing_getter)); format!("Indexing getter.\n\n") } InterfaceMethodKind::IndexingSetter => { extra_args.push(quote!(indexing_setter)); format!("Indexing setter.\n\n") } InterfaceMethodKind::IndexingDeleter => { extra_args.push(quote!(indexing_deleter)); format!("Indexing deleter.\n\n") } }; let mut features = BTreeSet::new(); for (_, ty) in arguments.iter() { add_features(&mut features, ty); } if let Some(ty) = ret_ty { add_features(&mut features, ty); } for parent in parents { features.remove(&parent.to_string()); } features.remove(&parent_name.to_string()); let cfg_features = get_cfg_features(options, &features); features.insert(parent_name.to_string()); let doc_comment = comment(doc_comment, &required_doc_string(options, &features)); let ret = ret_ty.as_ref().map(|ret| quote!( #ret )); let ret = if *catch { let ret = ret.unwrap_or_else(|| quote!(())); Some(quote!( Result<#ret, JsValue> )) } else { ret }; let ret = ret.as_ref().map(|ret| quote!( -> #ret )); let catch = if *catch { Some(quote!(catch,)) } else { None }; let (method, this) = if is_constructor { assert!(!is_static); (quote!(constructor,), None) } else if *is_static { (quote!( static_method_of = #parent_name, ), None) } else { let structural = if *structural { quote!(structural) } else { quote!(final) }; ( quote!( method, #structural, ), Some(quote!( this: &#parent_name, )), ) }; let arguments = generate_arguments(arguments, *variadic); let variadic = generate_variadic(*variadic); quote! { #unstable_attr #cfg_features #[wasm_bindgen( #catch #method #variadic #(#extra_args),* )] #doc_comment #unstable_docs pub fn #name(#this #(#arguments),*) #ret; } } } pub enum InterfaceConstValue { BooleanLiteral(bool), FloatLiteral(f64), SignedIntegerLiteral(i64), UnsignedIntegerLiteral(u64), } impl InterfaceConstValue { fn generate(&self) -> TokenStream { use InterfaceConstValue::*; match self { BooleanLiteral(false) => quote!(false), BooleanLiteral(true) => quote!(true), // the actual type is unknown because of typedefs // so we cannot use std::fxx::INFINITY // but we can use type inference FloatLiteral(f) if f.is_infinite() && f.is_sign_positive() => quote!(1.0 / 0.0), FloatLiteral(f) if f.is_infinite() && f.is_sign_negative() => quote!(-1.0 / 0.0), FloatLiteral(f) if f.is_nan() => quote!(0.0 / 0.0), // again no suffix // panics on +-inf, nan FloatLiteral(f) => { let f = Literal::f64_suffixed(*f); quote!(#f) } SignedIntegerLiteral(i) => { let i = Literal::i64_suffixed(*i); quote!(#i) } UnsignedIntegerLiteral(i) => { let i = Literal::u64_suffixed(*i); quote!(#i) } } } } pub struct InterfaceConst { pub name: Ident, pub js_name: String, pub ty: syn::Type, pub value: InterfaceConstValue, pub unstable: bool, } impl InterfaceConst { fn generate( &self, options: &Options, parent_name: &Ident, parent_js_name: &str, ) -> TokenStream { let name = &self.name; let ty = &self.ty; let js_name = &self.js_name; let value = self.value.generate(); let unstable = self.unstable; let unstable_attr = maybe_unstable_attr(unstable); let unstable_docs = maybe_unstable_docs(unstable); let doc_comment = comment( format!("The `{}.{}` const.", parent_js_name, js_name), &get_features_doc(options, parent_name.to_string()), ); quote! { #unstable_attr #doc_comment #unstable_docs pub const #name: #ty = #value as #ty; } } } pub struct Interface { pub name: Ident, pub js_name: String, pub deprecated: Option, pub has_interface: bool, pub parents: Vec, pub consts: Vec, pub attributes: Vec, pub methods: Vec, pub unstable: bool, } impl Interface { pub fn generate(&self, options: &Options) -> TokenStream { let Interface { name, js_name, deprecated, has_interface, parents, consts, attributes, methods, unstable, } = self; let unstable_attr = maybe_unstable_attr(*unstable); let unstable_docs = maybe_unstable_docs(*unstable); let doc_comment = comment( format!("The `{}` class.\n\n{}", name, mdn_doc(js_name, None)), &get_features_doc(options, name.to_string()), ); let deprecated = deprecated .as_ref() .map(|msg| quote!( #[deprecated(note = #msg)] )); let is_type_of = if *has_interface { None } else { Some(quote!(is_type_of = |_| false,)) }; let prefixes = if POLYFILL_INTERFACES.contains(js_name.as_str()) { Some(quote!(vendor_prefix = webkit,)) } else { None }; let extends = parents .into_iter() .map(|x| quote!( extends = #x, )) .collect::>(); let consts = consts .into_iter() .map(|x| x.generate(options, &name, js_name)) .collect::>(); let consts = if consts.is_empty() { None } else { Some(quote! { #unstable_attr impl #name { #(#deprecated #consts)* } }) }; let attributes = attributes .into_iter() .map(|x| x.generate(options, &name, js_name, &parents)) .collect::>(); let methods = methods .into_iter() .map(|x| x.generate(options, &name, js_name.to_string(), &parents)) .collect::>(); let js_ident = raw_ident(js_name); quote! { #![allow(unused_imports)] use super::*; use wasm_bindgen::prelude::*; #unstable_attr #[wasm_bindgen] extern "C" { #[wasm_bindgen( #is_type_of #prefixes #(#extends)* extends = ::js_sys::Object, js_name = #js_ident, typescript_type = #js_name )] #[derive(Debug, Clone, PartialEq, Eq)] #doc_comment #unstable_docs #deprecated pub type #name; #(#deprecated #attributes)* #(#deprecated #methods)* } #consts } } } pub struct DictionaryField { pub name: Ident, pub js_name: String, pub ty: Type, pub required: bool, pub unstable: bool, } impl DictionaryField { fn generate_rust(&self, options: &Options, parent_name: String) -> TokenStream { let DictionaryField { name, js_name, ty, required: _, unstable, } = self; let unstable_attr = maybe_unstable_attr(*unstable); let unstable_docs = maybe_unstable_docs(*unstable); let mut features = BTreeSet::new(); add_features(&mut features, ty); features.remove(&parent_name); let cfg_features = get_cfg_features(options, &features); features.insert(parent_name); let doc_comment = comment( format!("Change the `{}` field of this object.", js_name), &required_doc_string(options, &features), ); quote! { #unstable_attr #cfg_features #doc_comment #unstable_docs pub fn #name(&mut self, val: #ty) -> &mut Self { use wasm_bindgen::JsValue; let r = ::js_sys::Reflect::set( self.as_ref(), &JsValue::from(#js_name), &JsValue::from(val), ); debug_assert!(r.is_ok(), "setting properties should never fail on our dictionary objects"); let _ = r; self } } } } pub struct Dictionary { pub name: Ident, pub js_name: String, pub fields: Vec, pub unstable: bool, } impl Dictionary { pub fn generate(&self, options: &Options) -> TokenStream { let Dictionary { name, js_name, fields, unstable, } = self; let unstable_attr = maybe_unstable_attr(*unstable); let unstable_docs = maybe_unstable_docs(*unstable); let js_name = raw_ident(js_name); let mut required_features = BTreeSet::new(); let mut required_args = vec![]; let mut required_calls = vec![]; for field in fields.iter() { if field.required { let name = &field.name; let ty = &field.ty; required_args.push(quote!( #name: #ty )); required_calls.push(quote!( ret.#name(#name); )); add_features(&mut required_features, &field.ty); } } // The constructor is unstable if any of the fields are let (unstable_ctor, unstable_ctor_docs) = match unstable { true => (None, None), false => { let unstable = fields.iter().any(|f| f.unstable); (maybe_unstable_attr(unstable), maybe_unstable_docs(unstable)) } }; required_features.remove(&name.to_string()); let cfg_features = get_cfg_features(options, &required_features); required_features.insert(name.to_string()); let doc_comment = comment( format!("The `{}` dictionary.", name), &get_features_doc(options, name.to_string()), ); let ctor_doc_comment = comment( format!("Construct a new `{}`.", name), &required_doc_string(options, &required_features), ); let fields = fields .into_iter() .map(|field| field.generate_rust(options, name.to_string())) .collect::>(); let mut base_stream = quote! { #![allow(unused_imports)] use super::*; use wasm_bindgen::prelude::*; #unstable_attr #[wasm_bindgen] extern "C" { #[wasm_bindgen(extends = ::js_sys::Object, js_name = #js_name)] #[derive(Debug, Clone, PartialEq, Eq)] #doc_comment #unstable_docs pub type #name; } #unstable_attr impl #name { #unstable_ctor #cfg_features #ctor_doc_comment #unstable_docs #unstable_ctor_docs pub fn new(#(#required_args),*) -> Self { #[allow(unused_mut)] let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new()); #(#required_calls)* ret } #(#fields)* } }; if required_args.is_empty() { let default_impl = quote! { #unstable_attr impl Default for #name { fn default() -> Self { Self::new() } } }; base_stream.extend(default_impl.into_iter()); } base_stream } } pub struct Function { pub name: Ident, pub js_name: String, pub arguments: Vec<(Ident, Type)>, pub ret_ty: Option, pub catch: bool, pub variadic: bool, pub unstable: bool, } impl Function { fn generate( &self, options: &Options, parent_name: &Ident, parent_js_name: String, ) -> TokenStream { let Function { name, js_name, arguments, ret_ty, catch, variadic, unstable, } = self; let unstable_attr = maybe_unstable_attr(*unstable); let unstable_docs = maybe_unstable_docs(*unstable); let js_namespace = raw_ident(&parent_js_name); let doc_comment = format!( "The `{}.{}()` function.\n\n{}", parent_js_name, js_name, mdn_doc(&parent_js_name, Some(&js_name)) ); let mut features = BTreeSet::new(); for (_, ty) in arguments.iter() { add_features(&mut features, ty); } if let Some(ty) = ret_ty { add_features(&mut features, ty); } features.remove(&parent_name.to_string()); let cfg_features = get_cfg_features(options, &features); features.insert(parent_name.to_string()); let doc_comment = comment(doc_comment, &required_doc_string(options, &features)); let ret = ret_ty.as_ref().map(|ret| quote!( #ret )); let ret = if *catch { let ret = ret.unwrap_or_else(|| quote!(())); Some(quote!( Result<#ret, JsValue> )) } else { ret }; let ret = ret.as_ref().map(|ret| quote!( -> #ret )); let catch = if *catch { Some(quote!(catch,)) } else { None }; let arguments = generate_arguments(arguments, *variadic); let variadic = generate_variadic(*variadic); let js_name = raw_ident(js_name); quote! { #unstable_attr #cfg_features #[wasm_bindgen( #catch #variadic js_namespace = #js_namespace, js_name = #js_name )] #doc_comment #unstable_docs pub fn #name(#(#arguments),*) #ret; } } } pub struct Namespace { pub name: Ident, pub js_name: String, pub functions: Vec, } impl Namespace { pub fn generate(&self, options: &Options) -> TokenStream { let Namespace { name, js_name, functions, } = self; let functions = functions .into_iter() .map(|x| x.generate(options, &name, js_name.to_string())) .collect::>(); quote! { pub mod #name { #![allow(unused_imports)] use super::super::*; use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { #(#functions)* } } } } } wasm-bindgen-webidl-0.2.75/src/idl_type.rs000064400000000000000000000724260072674642500165470ustar 00000000000000use proc_macro2::{Ident, Span}; use syn; use wasm_bindgen_backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; use weedle::common::Identifier; use weedle::term; use weedle::types::*; use crate::first_pass::FirstPassRecord; use crate::util::{array, camel_case_ident, option_ty, shared_ref, snake_case_ident, TypePosition}; #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub(crate) enum IdlType<'a> { Boolean, Byte, Octet, Short, UnsignedShort, Long, UnsignedLong, LongLong, UnsignedLongLong, Float, UnrestrictedFloat, Double, UnrestrictedDouble, DomString, ByteString, UsvString, Object, Symbol, Error, Callback, ArrayBuffer, DataView, Int8Array { immutable: bool, }, Uint8Array { immutable: bool, }, Uint8ClampedArray { immutable: bool, }, Int16Array { immutable: bool, }, Uint16Array { immutable: bool, }, Int32Array { immutable: bool, }, Uint32Array { immutable: bool, }, Float32Array { immutable: bool, }, Float64Array { immutable: bool, }, ArrayBufferView { immutable: bool, }, BufferSource { immutable: bool, }, Interface(&'a str), Dictionary(&'a str), Enum(&'a str), CallbackInterface { name: &'a str, single_function: bool, }, Nullable(Box>), FrozenArray(Box>), Sequence(Box>), Promise(Box>), Record(Box>, Box>), Union(Vec>), Any, Undefined, UnknownInterface(&'a str), } pub(crate) trait ToIdlType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a>; } impl<'a> ToIdlType<'a> for UnionType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { let mut idl_types = Vec::with_capacity(self.body.list.len()); for t in &self.body.list { idl_types.push(t.to_idl_type(record)); } IdlType::Union(idl_types) } } impl<'a> ToIdlType<'a> for Type<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { Type::Single(t) => t.to_idl_type(record), Type::Union(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for SingleType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { SingleType::Any(t) => t.to_idl_type(record), SingleType::NonAny(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for NonAnyType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { NonAnyType::Promise(t) => t.to_idl_type(record), NonAnyType::Integer(t) => t.to_idl_type(record), NonAnyType::FloatingPoint(t) => t.to_idl_type(record), NonAnyType::Boolean(t) => t.to_idl_type(record), NonAnyType::Byte(t) => t.to_idl_type(record), NonAnyType::Octet(t) => t.to_idl_type(record), NonAnyType::ByteString(t) => t.to_idl_type(record), NonAnyType::DOMString(t) => t.to_idl_type(record), NonAnyType::USVString(t) => t.to_idl_type(record), NonAnyType::Sequence(t) => t.to_idl_type(record), NonAnyType::Object(t) => t.to_idl_type(record), NonAnyType::Symbol(t) => t.to_idl_type(record), NonAnyType::Error(t) => t.to_idl_type(record), NonAnyType::ArrayBuffer(t) => t.to_idl_type(record), NonAnyType::DataView(t) => t.to_idl_type(record), NonAnyType::Int8Array(t) => t.to_idl_type(record), NonAnyType::Int16Array(t) => t.to_idl_type(record), NonAnyType::Int32Array(t) => t.to_idl_type(record), NonAnyType::Uint8Array(t) => t.to_idl_type(record), NonAnyType::Uint16Array(t) => t.to_idl_type(record), NonAnyType::Uint32Array(t) => t.to_idl_type(record), NonAnyType::Uint8ClampedArray(t) => t.to_idl_type(record), NonAnyType::Float32Array(t) => t.to_idl_type(record), NonAnyType::Float64Array(t) => t.to_idl_type(record), NonAnyType::FrozenArrayType(t) => t.to_idl_type(record), NonAnyType::ArrayBufferView(t) => t.to_idl_type(record), NonAnyType::BufferSource(t) => t.to_idl_type(record), NonAnyType::RecordType(t) => t.to_idl_type(record), NonAnyType::Identifier(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for SequenceType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { IdlType::Sequence(Box::new(self.generics.body.to_idl_type(record))) } } impl<'a> ToIdlType<'a> for FrozenArrayType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { IdlType::FrozenArray(Box::new(self.generics.body.to_idl_type(record))) } } impl<'a, T: ToIdlType<'a>> ToIdlType<'a> for MayBeNull { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { let inner_idl_type = self.type_.to_idl_type(record); if self.q_mark.is_some() { IdlType::Nullable(Box::new(inner_idl_type)) } else { inner_idl_type } } } impl<'a> ToIdlType<'a> for PromiseType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { IdlType::Promise(Box::new(self.generics.body.to_idl_type(record))) } } impl<'a> ToIdlType<'a> for IntegerType { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { IntegerType::LongLong(t) => t.to_idl_type(record), IntegerType::Long(t) => t.to_idl_type(record), IntegerType::Short(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for LongLongType { fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> IdlType<'a> { if self.unsigned.is_some() { IdlType::UnsignedLongLong } else { IdlType::LongLong } } } impl<'a> ToIdlType<'a> for LongType { fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> IdlType<'a> { if self.unsigned.is_some() { IdlType::UnsignedLong } else { IdlType::Long } } } impl<'a> ToIdlType<'a> for ShortType { fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> IdlType<'a> { if self.unsigned.is_some() { IdlType::UnsignedShort } else { IdlType::Short } } } impl<'a> ToIdlType<'a> for FloatingPointType { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { FloatingPointType::Float(t) => t.to_idl_type(record), FloatingPointType::Double(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for FloatType { fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> IdlType<'a> { if self.unrestricted.is_some() { IdlType::UnrestrictedFloat } else { IdlType::Float } } } impl<'a> ToIdlType<'a> for DoubleType { fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> IdlType<'a> { if self.unrestricted.is_some() { IdlType::UnrestrictedDouble } else { IdlType::Double } } } impl<'a> ToIdlType<'a> for RecordType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { IdlType::Record( Box::new(self.generics.body.0.to_idl_type(record)), Box::new(self.generics.body.2.to_idl_type(record)), ) } } impl<'a> ToIdlType<'a> for StringType { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { StringType::Byte(t) => t.to_idl_type(record), StringType::DOM(t) => t.to_idl_type(record), StringType::USV(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for UnionMemberType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { UnionMemberType::Single(t) => t.to_idl_type(record), UnionMemberType::Union(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for ConstType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { ConstType::Integer(t) => t.to_idl_type(record), ConstType::FloatingPoint(t) => t.to_idl_type(record), ConstType::Boolean(t) => t.to_idl_type(record), ConstType::Byte(t) => t.to_idl_type(record), ConstType::Octet(t) => t.to_idl_type(record), ConstType::Identifier(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for ReturnType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { match self { ReturnType::Undefined(t) => t.to_idl_type(record), ReturnType::Type(t) => t.to_idl_type(record), } } } impl<'a> ToIdlType<'a> for AttributedType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { self.type_.to_idl_type(record) } } impl<'a> ToIdlType<'a> for AttributedNonAnyType<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { self.type_.to_idl_type(record) } } impl<'a> ToIdlType<'a> for Identifier<'a> { fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> IdlType<'a> { if self.0 == "DOMTimeStamp" { // https://heycam.github.io/webidl/#DOMTimeStamp IdlType::UnsignedLongLong } else if let Some(idl_type) = record.typedefs.get(&self.0) { idl_type.to_idl_type(record) } else if record.interfaces.contains_key(self.0) { IdlType::Interface(self.0) } else if record.dictionaries.contains_key(self.0) { IdlType::Dictionary(self.0) } else if record.enums.contains_key(self.0) { IdlType::Enum(self.0) } else if record.callbacks.contains(self.0) { IdlType::Callback } else if let Some(data) = record.callback_interfaces.get(self.0) { IdlType::CallbackInterface { name: self.0, single_function: data.single_function, } } else if self.0 == "WindowProxy" { // See this for more info: // // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy // // namely this seems to be "legalese" for "this is a `Window`", so // let's translate it as such. IdlType::Interface("Window") } else { log::warn!("Unrecognized type: {}", self.0); IdlType::UnknownInterface(self.0) } } } macro_rules! terms_to_idl_type { ($($t:tt => $r:tt)*) => ($( impl<'a> ToIdlType<'a> for term::$t { fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> IdlType<'a> { IdlType::$r } } )*); } // We default to arrays being mutable, but in certain cases where we're certain that // slices won't get mutated on the JS side (such as the WebGL APIs) we might, later in the flow, // instead use the immutable version. macro_rules! terms_to_idl_type_maybe_immutable { ($($t:tt => $r:tt)*) => ($( impl<'a> ToIdlType<'a> for term::$t { fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> IdlType<'a> { IdlType::$r { immutable: false } } } )*); } terms_to_idl_type! { Symbol => Symbol ByteString => ByteString DOMString => DomString USVString => UsvString Any => Any Boolean => Boolean Byte => Byte Double => Double Float => Float Long => Long Object => Object Octet => Octet Short => Short Undefined => Undefined ArrayBuffer => ArrayBuffer DataView => DataView Error => Error } terms_to_idl_type_maybe_immutable! { ArrayBufferView => ArrayBufferView BufferSource => BufferSource Float32Array => Float32Array Float64Array => Float64Array Int16Array => Int16Array Int32Array => Int32Array Int8Array => Int8Array Uint16Array => Uint16Array Uint32Array => Uint32Array Uint8Array => Uint8Array Uint8ClampedArray => Uint8ClampedArray } #[derive(Debug, Clone)] pub enum TypeError { CannotConvert, } impl<'a> IdlType<'a> { /// Generates a snake case type name. pub(crate) fn push_snake_case_name(&self, dst: &mut String) { match self { IdlType::Boolean => dst.push_str("bool"), IdlType::Byte => dst.push_str("i8"), IdlType::Octet => dst.push_str("u8"), IdlType::Short => dst.push_str("i16"), IdlType::UnsignedShort => dst.push_str("u16"), IdlType::Long => dst.push_str("i32"), IdlType::UnsignedLong => dst.push_str("u32"), IdlType::LongLong => dst.push_str("i64"), IdlType::UnsignedLongLong => dst.push_str("u64"), IdlType::Float | IdlType::UnrestrictedFloat => dst.push_str("f32"), IdlType::Double | IdlType::UnrestrictedDouble => dst.push_str("f64"), IdlType::DomString | IdlType::ByteString | IdlType::UsvString => dst.push_str("str"), IdlType::Object => dst.push_str("object"), IdlType::Symbol => dst.push_str("symbol"), IdlType::Error => dst.push_str("error"), IdlType::Callback => dst.push_str("callback"), IdlType::ArrayBuffer => dst.push_str("array_buffer"), IdlType::DataView => dst.push_str("data_view"), IdlType::Int8Array { .. } => dst.push_str("i8_array"), IdlType::Uint8Array { .. } => dst.push_str("u8_array"), IdlType::Uint8ClampedArray { .. } => dst.push_str("u8_clamped_array"), IdlType::Int16Array { .. } => dst.push_str("i16_array"), IdlType::Uint16Array { .. } => dst.push_str("u16_array"), IdlType::Int32Array { .. } => dst.push_str("i32_array"), IdlType::Uint32Array { .. } => dst.push_str("u32_array"), IdlType::Float32Array { .. } => dst.push_str("f32_array"), IdlType::Float64Array { .. } => dst.push_str("f64_array"), IdlType::ArrayBufferView { .. } => dst.push_str("array_buffer_view"), IdlType::BufferSource { .. } => dst.push_str("buffer_source"), IdlType::Interface(name) => dst.push_str(&snake_case_ident(name)), IdlType::UnknownInterface(name) => dst.push_str(&snake_case_ident(name)), IdlType::Dictionary(name) => dst.push_str(&snake_case_ident(name)), IdlType::Enum(name) => dst.push_str(&snake_case_ident(name)), IdlType::CallbackInterface { name, .. } => dst.push_str(&snake_case_ident(name)), IdlType::Nullable(idl_type) => { dst.push_str("opt_"); idl_type.push_snake_case_name(dst); } IdlType::FrozenArray(idl_type) => { idl_type.push_snake_case_name(dst); dst.push_str("_frozen_array"); } IdlType::Sequence(idl_type) => { idl_type.push_snake_case_name(dst); dst.push_str("_sequence"); } IdlType::Promise(idl_type) => { idl_type.push_snake_case_name(dst); dst.push_str("_promise"); } IdlType::Record(idl_type_from, idl_type_to) => { dst.push_str("record_from_"); idl_type_from.push_snake_case_name(dst); dst.push_str("_to_"); idl_type_to.push_snake_case_name(dst); } IdlType::Union(idl_types) => { dst.push_str("union_of_"); let mut first = true; for idl_type in idl_types { if first { first = false; } else { dst.push_str("_and_"); } idl_type.push_snake_case_name(dst); } } IdlType::Any => dst.push_str("any"), IdlType::Undefined => dst.push_str("undefined"), } } /// Converts to syn type if possible. pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Result, TypeError> { let externref = |ty| { Some(match pos { TypePosition::Argument => shared_ref(ty, false), TypePosition::Return => ty, }) }; let js_sys = |name: &str| { let path = vec![rust_ident("js_sys"), rust_ident(name)]; let ty = leading_colon_path_ty(path); externref(ty) }; let js_value = { let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; externref(leading_colon_path_ty(path)) }; match self { IdlType::Boolean => Ok(Some(ident_ty(raw_ident("bool")))), IdlType::Byte => Ok(Some(ident_ty(raw_ident("i8")))), IdlType::Octet => Ok(Some(ident_ty(raw_ident("u8")))), IdlType::Short => Ok(Some(ident_ty(raw_ident("i16")))), IdlType::UnsignedShort => Ok(Some(ident_ty(raw_ident("u16")))), IdlType::Long => Ok(Some(ident_ty(raw_ident("i32")))), IdlType::UnsignedLong => Ok(Some(ident_ty(raw_ident("u32")))), // Technically these are 64-bit numbers, but we're binding web // APIs that don't actually have return the corresponding 64-bit // type, `BigInt`. Instead the web basically uses floats for these // values. We already expand these types in argument position to // i32/f64 (convenience for i32, losslessness for f64). If we get // here then we're looking at an un-flattened long type such as // dictionary fields or return types. In order to generate bindings // for these functions we just use `f64` here, which should match // exactly what the JS web currently uses anyway. // // Perhaps one day we'll bind to u64/i64 here, but we need `BigInt` // to see more usage! IdlType::LongLong | IdlType::UnsignedLongLong => Ok(Some(ident_ty(raw_ident("f64")))), IdlType::Float => Ok(Some(ident_ty(raw_ident("f32")))), IdlType::UnrestrictedFloat => Ok(Some(ident_ty(raw_ident("f32")))), IdlType::Double => Ok(Some(ident_ty(raw_ident("f64")))), IdlType::UnrestrictedDouble => Ok(Some(ident_ty(raw_ident("f64")))), IdlType::DomString | IdlType::ByteString | IdlType::UsvString => match pos { TypePosition::Argument => Ok(Some(shared_ref(ident_ty(raw_ident("str")), false))), TypePosition::Return => Ok(Some(ident_ty(raw_ident("String")))), }, IdlType::Object => Ok(js_sys("Object")), IdlType::Symbol => Err(TypeError::CannotConvert), IdlType::Error => Err(TypeError::CannotConvert), IdlType::ArrayBuffer => Ok(js_sys("ArrayBuffer")), IdlType::DataView => Ok(js_sys("DataView")), IdlType::Int8Array { immutable } => Ok(Some(array("i8", pos, *immutable))), IdlType::Uint8Array { immutable } => Ok(Some(array("u8", pos, *immutable))), IdlType::Uint8ClampedArray { immutable } => { Ok(Some(clamped(array("u8", pos, *immutable)))) } IdlType::Int16Array { immutable } => Ok(Some(array("i16", pos, *immutable))), IdlType::Uint16Array { immutable } => Ok(Some(array("u16", pos, *immutable))), IdlType::Int32Array { immutable } => Ok(Some(array("i32", pos, *immutable))), IdlType::Uint32Array { immutable } => Ok(Some(array("u32", pos, *immutable))), IdlType::Float32Array { immutable } => Ok(Some(array("f32", pos, *immutable))), IdlType::Float64Array { immutable } => Ok(Some(array("f64", pos, *immutable))), IdlType::ArrayBufferView { .. } | IdlType::BufferSource { .. } => Ok(js_sys("Object")), IdlType::Interface(name) | IdlType::Dictionary(name) | IdlType::CallbackInterface { name, .. } => { let ty = ident_ty(rust_ident(camel_case_ident(name).as_str())); Ok(externref(ty)) } IdlType::Enum(name) => Ok(Some(ident_ty(rust_ident(camel_case_ident(name).as_str())))), IdlType::Nullable(idl_type) => { let inner = idl_type.to_syn_type(pos)?; match inner { Some(inner) => { // TODO: this is a bit of a hack, but `Option` isn't // supported right now. As a result if we see `JsValue` for our // inner type, leave that as the same when we create a nullable // version of that. That way `any?` just becomes `JsValue` and // it's up to users to dispatch and/or create instances // appropriately. if let syn::Type::Path(path) = &inner { if path.qself.is_none() && path .path .segments .last() .map(|p| p.ident == "JsValue") .unwrap_or(false) { return Ok(Some(inner.clone())); } } Ok(Some(option_ty(inner))) } None => Ok(None), } } // webidl sequences must always be returned as javascript `Array`s. They may accept // anything implementing the @@iterable interface. // The same implementation is fine for `FrozenArray` IdlType::FrozenArray(_idl_type) | IdlType::Sequence(_idl_type) => match pos { TypePosition::Argument => Ok(js_value), TypePosition::Return => Ok(js_sys("Array")), }, IdlType::Promise(_idl_type) => Ok(js_sys("Promise")), IdlType::Record(_idl_type_from, _idl_type_to) => Err(TypeError::CannotConvert), IdlType::Union(idl_types) => { // Note that most union types have already been expanded to // their components via `flatten`. Unions in a return position // or dictionary fields, however, haven't been flattened, which // means we may need to conver them to a `syn` type. // // Currently this does a bit of a "poor man's" tree traversal by // saying that if all union members are interfaces we can assume // they've all got `Object` as a superclass, so we can take an // object here. If any are not an interface though we // pessimisitcally translate the union into a `JsValue`, // absolutely anything. It's up to the application to figure out // what to do with that. // // TODO: we should probably do a better job here translating // unions to a single type. Two possible strategies could be: // // 1. Use strategy of finding the nearest common subclass // (finding the best type that is suitable for all values of // this union) instead of always assuming `Object`. // 2. Generate enum with payload in Rust for each union type. // Such an enum, however, might have a relatively high // overhead in creating it from a JS value, but would be // cheap to convert from a variant back to a JS value. if idl_types.iter().all(|idl_type| match idl_type { IdlType::Interface(..) => true, _ => false, }) { IdlType::Object.to_syn_type(pos) } else { IdlType::Any.to_syn_type(pos) } } IdlType::Any => Ok(js_value), IdlType::Undefined => Ok(None), IdlType::Callback => Ok(js_sys("Function")), IdlType::UnknownInterface(_) => Err(TypeError::CannotConvert), } } /// Flattens unions recursively. /// /// Works similarly to [flattened union member types], /// but also flattens unions inside generics of other types. /// /// [flattened union member types]: https://heycam.github.io/webidl/#dfn-flattened-union-member-types pub(crate) fn flatten(&self) -> Vec { match self { IdlType::Nullable(idl_type) => idl_type .flatten() .into_iter() .map(Box::new) .map(IdlType::Nullable) .collect(), IdlType::FrozenArray(idl_type) => idl_type .flatten() .into_iter() .map(Box::new) .map(IdlType::FrozenArray) .collect(), IdlType::Sequence(idl_type) => idl_type .flatten() .into_iter() .map(Box::new) .map(IdlType::Sequence) .collect(), IdlType::Promise(idl_type) => idl_type .flatten() .into_iter() .map(Box::new) .map(IdlType::Promise) .collect(), IdlType::Record(idl_type_from, idl_type_to) => { let mut idl_types = Vec::new(); for idl_type_from in idl_type_from.flatten() { for idl_type_to in idl_type_to.flatten() { idl_types.push(IdlType::Record( Box::new(idl_type_from.clone()), Box::new(idl_type_to.clone()), )); } } idl_types } IdlType::Union(idl_types) => idl_types .iter() .flat_map(|idl_type| idl_type.flatten()) .collect(), IdlType::ArrayBufferView { immutable } => vec![ IdlType::ArrayBufferView { immutable: *immutable, }, IdlType::Uint8Array { immutable: *immutable, }, ], IdlType::BufferSource { immutable } => vec![ IdlType::BufferSource { immutable: *immutable, }, IdlType::Uint8Array { immutable: *immutable, }, ], IdlType::LongLong => vec![IdlType::Long, IdlType::Double], IdlType::UnsignedLongLong => vec![IdlType::UnsignedLong, IdlType::Double], IdlType::CallbackInterface { name, single_function: true, } => { // According to the webidl spec [1] single-function callback // interfaces can also be replaced in arguments with simply a // single callable function, which we map to a `Callback`. // // [1]: https://heycam.github.io/webidl/#es-user-objects vec![ IdlType::Callback, IdlType::CallbackInterface { name, single_function: false, }, ] } idl_type @ _ => vec![idl_type.clone()], } } } #[test] fn idl_type_flatten_test() { use self::IdlType::*; assert_eq!( Union(vec![ Interface("Node"), Union(vec![Sequence(Box::new(Long),), Interface("Event"),]), Nullable(Box::new(Union(vec![ Interface("XMLHttpRequest"), DomString, ])),), Sequence(Box::new(Union(vec![ Sequence(Box::new(Double),), Interface("NodeList"), ])),), ]) .flatten(), vec![ Interface("Node"), Sequence(Box::new(Long)), Interface("Event"), Nullable(Box::new(Interface("XMLHttpRequest"))), Nullable(Box::new(DomString)), Sequence(Box::new(Sequence(Box::new(Double)))), Sequence(Box::new(Interface("NodeList"))), ], ); } /// From `T` create `::wasm_bindgen::Clamped` fn clamped(t: syn::Type) -> syn::Type { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Default::default(), args: vec![syn::GenericArgument::Type(t)].into_iter().collect(), gt_token: Default::default(), }); let ident = raw_ident("Clamped"); let seg = syn::PathSegment { ident, arguments }; syn::TypePath { qself: None, path: syn::Path { leading_colon: Some(Default::default()), segments: vec![Ident::new("wasm_bindgen", Span::call_site()).into(), seg] .into_iter() .collect(), }, } .into() } wasm-bindgen-webidl-0.2.75/src/lib.rs000064400000000000000000000643260072674642500155040ustar 00000000000000/*! # `wasm_bindgen_webidl` Converts WebIDL into wasm-bindgen's internal AST form, so that bindings can be emitted for the types and methods described in the WebIDL. */ #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-webidl/0.2")] mod constants; mod first_pass; mod generator; mod idl_type; mod traverse; mod util; use crate::first_pass::{CallbackInterfaceData, OperationData}; use crate::first_pass::{FirstPass, FirstPassRecord, InterfaceData, OperationId}; use crate::generator::{ Dictionary, DictionaryField, Enum, EnumVariant, Function, Interface, InterfaceAttribute, InterfaceAttributeKind, InterfaceConst, InterfaceMethod, Namespace, }; use crate::idl_type::ToIdlType; use crate::traverse::TraverseType; use crate::util::{ camel_case_ident, is_structural, is_type_unstable, read_dir, shouty_snake_case_ident, snake_case_ident, throws, webidl_const_v_to_backend_const_v, TypePosition, }; use anyhow::Context; use anyhow::Result; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use sourcefile::SourceFile; use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::ffi::OsStr; use std::fmt; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use wasm_bindgen_backend::util::rust_ident; use weedle::attribute::ExtendedAttributeList; use weedle::common::Identifier; use weedle::dictionary::DictionaryMember; use weedle::interface::InterfaceMember; use weedle::Parse; /// Options to configure the conversion process #[derive(Debug)] pub struct Options { /// Whether to generate cfg features or not pub features: bool, } #[derive(Default)] struct Program { tokens: TokenStream, required_features: BTreeSet, } impl Program { fn to_string(&self) -> Option { if self.tokens.is_empty() { None } else { Some(self.tokens.to_string()) } } } /// A parse error indicating where parsing failed #[derive(Debug)] pub struct WebIDLParseError(pub usize); impl fmt::Display for WebIDLParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "failed to parse webidl at byte position {}", self.0) } } impl std::error::Error for WebIDLParseError {} #[derive(Clone, Copy, PartialEq)] pub(crate) enum ApiStability { Stable, Unstable, } impl ApiStability { pub(crate) fn is_unstable(self) -> bool { self == Self::Unstable } } impl Default for ApiStability { fn default() -> Self { Self::Stable } } fn parse_source(source: &str) -> Result> { match weedle::Definitions::parse(source) { Ok(("", parsed)) => Ok(parsed), Ok((remaining, _)) | Err(weedle::Err::Error((remaining, _))) | Err(weedle::Err::Failure((remaining, _))) => { Err(WebIDLParseError(source.len() - remaining.len()).into()) } Err(weedle::Err::Incomplete(needed)) => { Err(anyhow::anyhow!("needed {:?} more bytes", needed)) } } } /// Parse a string of WebIDL source text into a wasm-bindgen AST. fn parse( webidl_source: &str, unstable_source: &str, options: Options, ) -> Result> { let mut first_pass_record: FirstPassRecord = Default::default(); let definitions = parse_source(webidl_source)?; definitions.first_pass(&mut first_pass_record, ApiStability::Stable)?; let unstable_definitions = parse_source(unstable_source)?; // Gather unstable type Identifiers so that stable APIs can be downgraded // to unstable if they accept one of these types let unstable_types: HashSet = unstable_definitions .iter() .flat_map(|definition| { use weedle::Definition::*; match definition { Dictionary(v) => Some(v.identifier), Enum(v) => Some(v.identifier), Interface(v) => Some(v.identifier), _ => None, } }) .collect(); unstable_definitions.first_pass(&mut first_pass_record, ApiStability::Unstable)?; let mut types: BTreeMap = BTreeMap::new(); for (js_name, e) in first_pass_record.enums.iter() { let name = rust_ident(&camel_case_ident(js_name)); let program = types.entry(name.to_string()).or_default(); first_pass_record.append_enum(&options, program, name, js_name, e); } for (js_name, d) in first_pass_record.dictionaries.iter() { let name = rust_ident(&camel_case_ident(js_name)); let program = types.entry(name.to_string()).or_default(); first_pass_record.append_dictionary( &options, program, name, js_name.to_string(), d, &unstable_types, ); } for (js_name, n) in first_pass_record.namespaces.iter() { let name = rust_ident(&snake_case_ident(js_name)); let program = types.entry(name.to_string()).or_default(); first_pass_record.append_ns(&options, program, name, js_name.to_string(), n); } for (js_name, d) in first_pass_record.interfaces.iter() { let name = rust_ident(&camel_case_ident(js_name)); let program = types.entry(name.to_string()).or_default(); first_pass_record.append_interface( &options, program, name, js_name.to_string(), &unstable_types, d, ); } for (js_name, d) in first_pass_record.callback_interfaces.iter() { let name = rust_ident(&camel_case_ident(js_name)); let program = types.entry(name.to_string()).or_default(); first_pass_record.append_callback_interface( &options, program, name, js_name.to_string(), d, ); } Ok(types) } /// Data for a single feature #[derive(Debug)] pub struct Feature { /// Generated code pub code: String, /// Required features pub required_features: Vec, } /// Compile the given WebIDL source text into Rust source text containing /// `wasm-bindgen` bindings to the things described in the WebIDL. pub fn compile( webidl_source: &str, experimental_source: &str, options: Options, ) -> Result> { let ast = parse(webidl_source, experimental_source, options)?; let features = ast .into_iter() .filter_map(|(name, program)| { let code = program.to_string()?; let required_features = program.required_features.into_iter().collect(); Some(( name, Feature { required_features, code, }, )) }) .collect(); Ok(features) } impl<'src> FirstPassRecord<'src> { fn append_enum( &self, options: &Options, program: &mut Program, name: Ident, js_name: &str, data: &first_pass::EnumData<'src>, ) { let enum_ = data.definition; let unstable = data.stability.is_unstable(); assert_eq!(js_name, enum_.identifier.0); let variants = enum_ .values .body .list .iter() .map(|v| { let name = if !v.0.is_empty() { rust_ident(camel_case_ident(&v.0).as_str()) } else { rust_ident("None") }; let value = v.0.to_string(); EnumVariant { name, value } }) .collect::>(); Enum { name, variants, unstable, } .generate(options) .to_tokens(&mut program.tokens); } // tons more data for what's going on here at // https://www.w3.org/TR/WebIDL-1/#idl-dictionaries fn append_dictionary( &self, options: &Options, program: &mut Program, name: Ident, js_name: String, data: &first_pass::DictionaryData<'src>, unstable_types: &HashSet, ) { let def = match data.definition { Some(def) => def, None => return, }; assert_eq!(js_name, def.identifier.0); let unstable = data.stability.is_unstable(); let mut fields = Vec::new(); if !self.append_dictionary_members(&js_name, &mut fields, unstable, unstable_types) { return; } Dictionary { name, js_name, fields, unstable, } .generate(options) .to_tokens(&mut program.tokens); } fn append_dictionary_members( &self, dict: &'src str, dst: &mut Vec, unstable: bool, unstable_types: &HashSet, ) -> bool { let dict_data = &self.dictionaries[&dict]; let definition = dict_data.definition.unwrap(); // > The order of the dictionary members on a given dictionary is // > such that inherited dictionary members are ordered before // > non-inherited members ... if let Some(parent) = &definition.inheritance { if !self.append_dictionary_members(parent.identifier.0, dst, unstable, unstable_types) { return false; } } // > ... and the dictionary members on the one dictionary // > definition (including any partial dictionary definitions) are // > ordered lexicographically by the Unicode codepoints that // > comprise their identifiers. let start = dst.len(); let members = definition.members.body.iter(); let partials = dict_data.partials.iter().flat_map(|d| &d.members.body); for member in members.chain(partials) { match self.dictionary_field(member, unstable, unstable_types) { Some(f) => dst.push(f), None => { log::warn!( "unsupported dictionary field {:?}", (dict, member.identifier.0), ); // If this is required then we can't support the // dictionary at all, but if it's not required we can // avoid generating bindings for the field and keep // going otherwise. if member.required.is_some() { return false; } } } } dst[start..].sort_by_key(|f| f.js_name.clone()); return true; } fn dictionary_field( &self, field: &'src DictionaryMember<'src>, unstable: bool, unstable_types: &HashSet, ) -> Option { let unstable_override = match unstable { true => true, false => is_type_unstable(&field.type_, unstable_types), }; // use argument position now as we're just binding setters let ty = field .type_ .to_idl_type(self) .to_syn_type(TypePosition::Argument) .unwrap_or(None)?; // Slice types aren't supported because they don't implement // `Into` match ty { syn::Type::Reference(ref i) => match &*i.elem { syn::Type::Slice(_) => return None, _ => (), }, syn::Type::Path(ref path, ..) => // check that our inner don't contains slices either { for seg in path.path.segments.iter() { if let syn::PathArguments::AngleBracketed(ref arg) = seg.arguments { for elem in &arg.args { if let syn::GenericArgument::Type(syn::Type::Reference(ref i)) = elem { match &*i.elem { syn::Type::Slice(_) => return None, _ => (), } } } } } } _ => (), }; // Similarly i64/u64 aren't supported because they don't // implement `Into` let mut any_64bit = false; ty.traverse_type(&mut |ident| { if !any_64bit { if ident == "u64" || ident == "i64" { any_64bit = true; } } }); if any_64bit { return None; } Some(DictionaryField { required: field.required.is_some(), name: rust_ident(&snake_case_ident(field.identifier.0)), js_name: field.identifier.0.to_string(), ty, unstable: unstable_override, }) } fn append_ns( &'src self, options: &Options, program: &mut Program, name: Ident, js_name: String, ns: &'src first_pass::NamespaceData<'src>, ) { let mut functions = vec![]; for (id, data) in ns.operations.iter() { self.append_ns_member(&mut functions, &js_name, id, data); } if !functions.is_empty() { Namespace { name, js_name, functions, } .generate(options) .to_tokens(&mut program.tokens); } } fn append_ns_member( &self, functions: &mut Vec, js_name: &'src str, id: &OperationId<'src>, data: &OperationData<'src>, ) { match id { OperationId::Operation(Some(_)) => {} OperationId::Constructor(_) | OperationId::NamedConstructor(_) | OperationId::Operation(None) | OperationId::IndexingGetter | OperationId::IndexingSetter | OperationId::IndexingDeleter => { log::warn!("Unsupported unnamed operation: on {:?}", js_name); return; } } for x in self.create_imports(None, id, data, false, &HashSet::new()) { functions.push(Function { name: x.name, js_name: x.js_name, arguments: x.arguments, ret_ty: x.ret_ty, catch: x.catch, variadic: x.variadic, unstable: false, }); } } fn append_const( &self, consts: &mut Vec, member: &'src weedle::interface::ConstMember<'src>, unstable: bool, ) { let idl_type = member.const_type.to_idl_type(self); let ty = idl_type.to_syn_type(TypePosition::Return).unwrap().unwrap(); let js_name = member.identifier.0; let name = rust_ident(shouty_snake_case_ident(js_name).as_str()); let value = webidl_const_v_to_backend_const_v(&member.const_value); consts.push(InterfaceConst { name, js_name: js_name.to_string(), ty, value, unstable, }); } fn append_interface( &self, options: &Options, program: &mut Program, name: Ident, js_name: String, unstable_types: &HashSet, data: &InterfaceData<'src>, ) { let unstable = data.stability.is_unstable(); let has_interface = data.has_interface; let deprecated = data.deprecated.clone(); let parents = self .all_superclasses(&js_name) .map(|parent| { let ident = rust_ident(&camel_case_ident(&parent)); program.required_features.insert(parent); ident }) .collect::>(); let mut consts = vec![]; let mut attributes = vec![]; let mut methods = vec![]; for member in data.consts.iter() { self.append_const(&mut consts, member, unstable); } for member in data.attributes.iter() { let unstable = unstable || member.stability.is_unstable(); let member = member.definition; self.member_attribute( &mut attributes, member.modifier, member.readonly.is_some(), &member.type_, member.identifier.0.to_string(), &member.attributes, data.definition_attributes, unstable, ); } for (id, op_data) in data.operations.iter() { self.member_operation(&mut methods, data, id, op_data, unstable_types); } for mixin_data in self.all_mixins(&js_name) { for member in &mixin_data.consts { self.append_const(&mut consts, member, unstable); } for member in &mixin_data.attributes { let unstable = unstable || member.stability.is_unstable(); let member = member.definition; self.member_attribute( &mut attributes, if let Some(s) = member.stringifier { Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s)) } else { None }, member.readonly.is_some(), &member.type_, member.identifier.0.to_string(), &member.attributes, data.definition_attributes, unstable, ); } for (id, op_data) in mixin_data.operations.iter() { self.member_operation(&mut methods, data, id, op_data, unstable_types); } } Interface { name, js_name, deprecated, has_interface, parents, consts, attributes, methods, unstable, } .generate(options) .to_tokens(&mut program.tokens); } fn member_attribute( &self, attributes: &mut Vec, modifier: Option, readonly: bool, type_: &'src weedle::types::AttributedType<'src>, js_name: String, attrs: &'src Option>, container_attrs: Option<&'src ExtendedAttributeList<'src>>, unstable: bool, ) { use weedle::interface::StringifierOrInheritOrStatic::*; let is_static = match modifier { Some(Stringifier(_)) => unreachable!(), // filtered out earlier Some(Inherit(_)) => false, Some(Static(_)) => true, None => false, }; let structural = is_structural(attrs.as_ref(), container_attrs); let catch = throws(attrs); let ty = type_ .type_ .to_idl_type(self) .to_syn_type(TypePosition::Return) .unwrap_or(None); // Skip types which can't be converted if let Some(ty) = ty { let kind = InterfaceAttributeKind::Getter; attributes.push(InterfaceAttribute { is_static, structural, catch, ty, js_name: js_name.clone(), kind, unstable, }); } if !readonly { let ty = type_ .type_ .to_idl_type(self) .to_syn_type(TypePosition::Argument) .unwrap_or(None); // Skip types which can't be converted if let Some(ty) = ty { let kind = InterfaceAttributeKind::Setter; attributes.push(InterfaceAttribute { is_static, structural, catch, ty, js_name, kind, unstable, }); } } } fn member_operation( &self, methods: &mut Vec, data: &InterfaceData<'src>, id: &OperationId<'src>, op_data: &OperationData<'src>, unstable_types: &HashSet, ) { let attrs = data.definition_attributes; let unstable = data.stability.is_unstable(); for method in self.create_imports(attrs, id, op_data, unstable, unstable_types) { methods.push(method); } } fn append_callback_interface( &self, options: &Options, program: &mut Program, name: Ident, js_name: String, item: &CallbackInterfaceData<'src>, ) { assert_eq!(js_name, item.definition.identifier.0); let mut fields = Vec::new(); for member in item.definition.members.body.iter() { match member { InterfaceMember::Operation(op) => { let identifier = match op.identifier { Some(i) => i.0, None => continue, }; let pos = TypePosition::Argument; fields.push(DictionaryField { required: false, name: rust_ident(&snake_case_ident(identifier)), js_name: identifier.to_string(), ty: idl_type::IdlType::Callback .to_syn_type(pos) .unwrap() .unwrap(), unstable: false, }) } _ => { log::warn!( "skipping callback interface member on {}", item.definition.identifier.0 ); } } } Dictionary { name, js_name, fields, unstable: false, } .generate(options) .to_tokens(&mut program.tokens); } } /// Generates Rust source code with #[wasm_bindgen] annotations. /// /// * Reads WebIDL files in `from` /// * Generates Rust source code in the directory `to` /// * `options.features` indicates whether everything is gated by features or /// not /// /// If features are enabled, returns a string that should be appended to /// `Cargo.toml` which lists all the known features. pub fn generate(from: &Path, to: &Path, options: Options) -> Result { let generate_features = options.features; let source = read_source_from_path(&from.join("enabled"))?; let unstable_source = read_source_from_path(&from.join("unstable"))?; let features = parse_webidl(generate_features, source, unstable_source)?; if to.exists() { fs::remove_dir_all(&to).context("Removing features directory")?; } fs::create_dir_all(&to).context("Creating features directory")?; for (name, feature) in features.iter() { let out_file_path = to.join(format!("gen_{}.rs", name)); fs::write(&out_file_path, &feature.code)?; rustfmt(&out_file_path, name)?; } let binding_file = features.keys().map(|name| { if generate_features { format!("#[cfg(feature = \"{name}\")] #[allow(non_snake_case)] mod gen_{name};\n#[cfg(feature = \"{name}\")] pub use gen_{name}::*;", name = name) } else { format!("#[allow(non_snake_case)] mod gen_{name};\npub use gen_{name}::*;", name = name) } }).collect::>().join("\n\n"); fs::write(to.join("mod.rs"), binding_file)?; rustfmt(&to.join("mod.rs"), "mod")?; return if generate_features { let features = features .iter() .map(|(name, feature)| { let features = feature .required_features .iter() .map(|x| format!("\"{}\"", x)) .collect::>() .join(", "); format!("{} = [{}]", name, features) }) .collect::>() .join("\n"); Ok(features) } else { Ok(String::new()) }; /// Read all WebIDL files in a directory into a single `SourceFile` fn read_source_from_path(dir: &Path) -> Result { let entries = read_dir(dir).context("reading webidls directory")?; let mut source = SourceFile::default(); for path in entries { if path.extension() != Some(OsStr::new("webidl")) { continue; } source = source .add_file(&path) .with_context(|| format!("reading contents of file \"{}\"", path.display()))?; } Ok(source) } fn rustfmt(path: &PathBuf, name: &str) -> Result<()> { // run rustfmt on the generated file - really handy for debugging let result = Command::new("rustfmt") .arg("--edition") .arg("2018") .arg(&path) .status() .context(format!("rustfmt on file {}", name))?; assert!(result.success(), "rustfmt on file {}", name); Ok(()) } fn parse_webidl( generate_features: bool, enabled: SourceFile, unstable: SourceFile, ) -> Result> { let options = Options { features: generate_features, }; match compile(&enabled.contents, &unstable.contents, options) { Ok(features) => Ok(features), Err(e) => { if let Some(err) = e.downcast_ref::() { if let Some(pos) = enabled.resolve_offset(err.0) { let ctx = format!( "compiling WebIDL into wasm-bindgen bindings in file \ \"{}\", line {} column {}", pos.filename, pos.line + 1, pos.col + 1 ); return Err(e.context(ctx)); } else { return Err(e.context("compiling WebIDL into wasm-bindgen bindings")); } } return Err(e.context("compiling WebIDL into wasm-bindgen bindings")); } } } } wasm-bindgen-webidl-0.2.75/src/main.rs000064400000000000000000000015450072674642500156540ustar 00000000000000use anyhow::{Context, Result}; use std::fs; use std::path::PathBuf; use structopt::StructOpt; #[derive(StructOpt, Debug)] #[structopt( name = "wasm-bindgen-webidl", about = "Converts WebIDL into wasm-bindgen compatible code." )] struct Opt { #[structopt(parse(from_os_str))] input_dir: PathBuf, #[structopt(parse(from_os_str))] output_dir: PathBuf, #[structopt(long)] no_features: bool, } fn main() -> Result<()> { env_logger::init(); let opt = Opt::from_args(); let features = !opt.no_features; let generated_features = wasm_bindgen_webidl::generate( &opt.input_dir, &opt.output_dir, wasm_bindgen_webidl::Options { features }, )?; if features { fs::write(&"features", generated_features) .context("writing features to current directory")?; } Ok(()) } wasm-bindgen-webidl-0.2.75/src/traverse.rs000064400000000000000000000103460072674642500165620ustar 00000000000000use syn::{Ident, Type}; pub trait TraverseType { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident); } impl TraverseType for Type { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { match self { Type::Array(x) => x.traverse_type(f), Type::BareFn(x) => x.traverse_type(f), Type::Group(x) => x.traverse_type(f), Type::Paren(x) => x.traverse_type(f), Type::Path(x) => x.traverse_type(f), Type::Ptr(x) => x.traverse_type(f), Type::Reference(x) => x.traverse_type(f), Type::Slice(x) => x.traverse_type(f), Type::Tuple(x) => x.traverse_type(f), _ => {} } } } impl TraverseType for syn::TypeArray { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.elem.traverse_type(f); } } impl TraverseType for syn::TypeBareFn { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { for input in self.inputs.iter() { input.ty.traverse_type(f); } self.output.traverse_type(f); } } impl TraverseType for syn::ReturnType { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { match self { Self::Default => {} Self::Type(_, ty) => { ty.traverse_type(f); } } } } impl TraverseType for syn::TypeGroup { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.elem.traverse_type(f); } } impl TraverseType for syn::TypeParen { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.elem.traverse_type(f); } } impl TraverseType for syn::TypePath { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { if let Some(qself) = self.qself.as_ref() { qself.traverse_type(f); } if let Some(last) = self.path.segments.last() { f(&last.ident); last.arguments.traverse_type(f); } } } impl TraverseType for syn::QSelf { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.ty.traverse_type(f); } } impl TraverseType for syn::PathArguments { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { match self { Self::None => {} Self::AngleBracketed(x) => x.traverse_type(f), Self::Parenthesized(x) => x.traverse_type(f), } } } impl TraverseType for syn::AngleBracketedGenericArguments { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { for ty in self.args.iter() { ty.traverse_type(f); } } } impl TraverseType for syn::GenericArgument { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { match self { Self::Type(x) => x.traverse_type(f), Self::Binding(x) => x.traverse_type(f), _ => {} } } } impl TraverseType for syn::Binding { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.ty.traverse_type(f); } } impl TraverseType for syn::ParenthesizedGenericArguments { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { for ty in self.inputs.iter() { ty.traverse_type(f); } self.output.traverse_type(f); } } impl TraverseType for syn::TypePtr { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.elem.traverse_type(f); } } impl TraverseType for syn::TypeReference { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.elem.traverse_type(f); } } impl TraverseType for syn::TypeTuple { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { for ty in self.elems.iter() { ty.traverse_type(f); } } } impl TraverseType for syn::TypeSlice { fn traverse_type(&self, f: &mut F) where F: FnMut(&Ident), { self.elem.traverse_type(f); } } wasm-bindgen-webidl-0.2.75/src/util.rs000064400000000000000000000627660072674642500157210ustar 00000000000000use std::collections::{BTreeSet, HashSet}; use std::fs; use std::iter::FromIterator; use std::path::{Path, PathBuf}; use std::ptr; use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn; use wasm_bindgen_backend::util::{ident_ty, raw_ident, rust_ident}; use weedle; use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList, IdentifierOrString}; use weedle::common::Identifier; use weedle::literal::{ConstValue, FloatLit, IntegerLit}; use weedle::types::{NonAnyType, SingleType}; use crate::constants::IMMUTABLE_SLICE_WHITELIST; use crate::first_pass::{FirstPassRecord, OperationData, OperationId, Signature}; use crate::generator::{InterfaceConstValue, InterfaceMethod, InterfaceMethodKind}; use crate::idl_type::{IdlType, ToIdlType}; use crate::Options; /// For variadic operations an overload with a `js_sys::Array` argument is generated alongside with /// `operation_name_0`, `operation_name_1`, `operation_name_2`, ..., `operation_name_n` overloads /// which have the count of arguments for passing values to the variadic argument /// in their names, where `n` is this constant. const MAX_VARIADIC_ARGUMENTS_COUNT: usize = 7; /// Similar to std::fs::read_dir except it returns a sorted Vec, /// which is important to make the code generation deterministic. pub(crate) fn read_dir

(path: P) -> std::io::Result> where P: AsRef, { let mut entries = fs::read_dir(path)? .map(|entry| Ok(entry?.path())) .collect::>>()?; entries.sort(); Ok(entries) } /// Take a type and create an immutable shared reference to that type. pub(crate) fn shared_ref(ty: syn::Type, mutable: bool) -> syn::Type { syn::TypeReference { and_token: Default::default(), lifetime: None, mutability: if mutable { Some(syn::token::Mut::default()) } else { None }, elem: Box::new(ty), } .into() } /// Fix case of identifiers like `HTMLBRElement` or `texImage2D` fn fix_ident(identifier: &str) -> String { identifier .replace("HTML", "HTML_") .replace("1D", "_1d") .replace("2D", "_2d") .replace("3D", "_3d") } /// Convert an identifier to camel case pub fn camel_case_ident(identifier: &str) -> String { fix_ident(identifier).to_camel_case() } /// Convert an identifier to shouty snake case pub fn shouty_snake_case_ident(identifier: &str) -> String { fix_ident(identifier).to_shouty_snake_case() } /// Convert an identifier to snake case pub fn snake_case_ident(identifier: &str) -> String { fix_ident(identifier).to_snake_case() } // Returns a link to MDN pub fn mdn_doc(class: &str, method: Option<&str>) -> String { let mut link = format!("https://developer.mozilla.org/en-US/docs/Web/API/{}", class); if let Some(method) = method { link.push_str(&format!("/{}", method)); } format!("[MDN Documentation]({})", link).into() } // Array type is borrowed for arguments (`&mut [T]` or `&[T]`) and owned for return value (`Vec`). pub(crate) fn array(base_ty: &str, pos: TypePosition, immutable: bool) -> syn::Type { match pos { TypePosition::Argument => { shared_ref( slice_ty(ident_ty(raw_ident(base_ty))), /*mutable =*/ !immutable, ) } TypePosition::Return => vec_ty(ident_ty(raw_ident(base_ty))), } } /// Map a webidl const value to the correct wasm-bindgen const value pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> InterfaceConstValue { use std::f64::{INFINITY, NAN, NEG_INFINITY}; match *v { ConstValue::Boolean(b) => InterfaceConstValue::BooleanLiteral(b.0), ConstValue::Float(FloatLit::NegInfinity(_)) => { InterfaceConstValue::FloatLiteral(NEG_INFINITY) } ConstValue::Float(FloatLit::Infinity(_)) => InterfaceConstValue::FloatLiteral(INFINITY), ConstValue::Float(FloatLit::NaN(_)) => InterfaceConstValue::FloatLiteral(NAN), ConstValue::Float(FloatLit::Value(s)) => { InterfaceConstValue::FloatLiteral(s.0.parse().unwrap()) } ConstValue::Integer(lit) => { let mklit = |orig_text: &str, base: u32, offset: usize| { let (negative, text) = if orig_text.starts_with("-") { (true, &orig_text[1..]) } else { (false, orig_text) }; if text == "0" { return InterfaceConstValue::SignedIntegerLiteral(0); } let text = &text[offset..]; let n = u64::from_str_radix(text, base) .unwrap_or_else(|_| panic!("literal too big: {}", orig_text)); if negative { let n = if n > (i64::min_value() as u64).wrapping_neg() { panic!("literal too big: {}", orig_text) } else { n.wrapping_neg() as i64 }; InterfaceConstValue::SignedIntegerLiteral(n) } else { InterfaceConstValue::UnsignedIntegerLiteral(n) } }; match lit { IntegerLit::Hex(h) => mklit(h.0, 16, 2), // leading 0x IntegerLit::Oct(h) => mklit(h.0, 8, 1), // leading 0 IntegerLit::Dec(h) => mklit(h.0, 10, 0), } } ConstValue::Null(_) => unimplemented!(), } } /// From `T` create `[T]`. pub(crate) fn slice_ty(t: syn::Type) -> syn::Type { syn::TypeSlice { bracket_token: Default::default(), elem: Box::new(t), } .into() } /// From `T` create `Vec`. pub(crate) fn vec_ty(t: syn::Type) -> syn::Type { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Default::default(), args: FromIterator::from_iter(vec![syn::GenericArgument::Type(t)]), gt_token: Default::default(), }); let ident = raw_ident("Vec"); let seg = syn::PathSegment { ident, arguments }; let path: syn::Path = seg.into(); let ty = syn::TypePath { qself: None, path }; ty.into() } /// From `T` create `Option` pub(crate) fn option_ty(t: syn::Type) -> syn::Type { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Default::default(), args: FromIterator::from_iter(vec![syn::GenericArgument::Type(t)]), gt_token: Default::default(), }); let ident = raw_ident("Option"); let seg = syn::PathSegment { ident, arguments }; let path: syn::Path = seg.into(); let ty = syn::TypePath { qself: None, path }; ty.into() } /// Possible positions for a type in a function signature. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TypePosition { Argument, Return, } impl<'src> FirstPassRecord<'src> { pub fn create_imports( &self, container_attrs: Option<&ExtendedAttributeList<'src>>, id: &OperationId<'src>, data: &OperationData<'src>, unstable: bool, unstable_types: &HashSet, ) -> Vec { let is_static = data.is_static; // First up, prune all signatures that reference unsupported arguments. // We won't consider these until said arguments are implemented. // // Note that we handle optional arguments as well. Optional arguments // should only appear at the end of argument lists and when we see one // we can simply push our signature so far onto the list for the // signature where that and all remaining optional arguments are // undefined. let mut signatures = Vec::new(); for signature in data.signatures.iter() { let mut idl_args = Vec::with_capacity(signature.args.len()); for (i, arg) in signature.args.iter().enumerate() { if arg.optional { assert!( signature.args[i..] .iter() .all(|arg| arg.optional || arg.variadic), "Not optional or variadic argument after optional argument: {:?}", signature.args, ); signatures.push((signature, idl_args.clone())); } let idl_type = arg.ty.to_idl_type(self); let idl_type = self.maybe_adjust(idl_type, id); idl_args.push(idl_type); } signatures.push((signature, idl_args)); } // Next expand all the signatures in `data` into all signatures that // we're going to generate. These signatures will be used to determine // the names for all the various functions. #[derive(Clone)] struct ExpandedSig<'a> { orig: &'a Signature<'a>, args: Vec>, } let mut actual_signatures = Vec::new(); for (signature, idl_args) in signatures.iter() { let start = actual_signatures.len(); // Start off with an empty signature, this'll handle zero-argument // cases and otherwise the loop below will continue to add on to this. actual_signatures.push(ExpandedSig { orig: signature, args: Vec::with_capacity(signature.args.len()), }); for (i, idl_type) in idl_args.iter().enumerate() { // small sanity check assert!(start < actual_signatures.len()); for sig in actual_signatures[start..].iter() { assert_eq!(sig.args.len(), i); } // The first element of the flattened type gets pushed directly // in-place, but all other flattened types will cause new // signatures to be created. let cur = actual_signatures.len(); for (j, idl_type) in idl_type.flatten().into_iter().enumerate() { for k in start..cur { if j == 0 { actual_signatures[k].args.push(idl_type.clone()); } else { let mut sig = actual_signatures[k].clone(); assert_eq!(sig.args.len(), i + 1); sig.args.truncate(i); sig.args.push(idl_type.clone()); actual_signatures.push(sig); } } } } } let (name, kind, force_structural, force_throws) = match id { // Constructors aren't annotated with `[Throws]` extended attributes // (how could they be, since they themselves are extended // attributes?) so we must conservatively assume that they can // always throw. // // From https://heycam.github.io/webidl/#Constructor (emphasis // mine): // // > The prose definition of a constructor must either return an IDL // > value of a type corresponding to the interface the // > `[Constructor]` extended attribute appears on, **or throw an // > exception**. OperationId::Constructor(_) => { ("new", InterfaceMethodKind::Constructor(None), false, true) } OperationId::NamedConstructor(n) => ( "new", InterfaceMethodKind::Constructor(Some(n.0.to_string())), false, true, ), OperationId::Operation(Some(s)) => (*s, InterfaceMethodKind::Regular, false, false), OperationId::Operation(None) => { log::warn!("unsupported unnamed operation"); return Vec::new(); } OperationId::IndexingGetter => { ("get", InterfaceMethodKind::IndexingGetter, true, false) } OperationId::IndexingSetter => { ("set", InterfaceMethodKind::IndexingSetter, true, false) } OperationId::IndexingDeleter => { ("delete", InterfaceMethodKind::IndexingDeleter, true, false) } }; let mut ret = Vec::new(); for signature in actual_signatures.iter() { // Ignore signatures with invalid return types // // TODO: overloads probably never change return types, so we should // do this much earlier to avoid all the above work if // possible. let ret_ty = signature.orig.ret.to_idl_type(self); let mut rust_name = snake_case_ident(name); let mut first = true; for (i, arg) in signature.args.iter().enumerate() { // Find out if any other known signature either has the same // name for this argument or a different type for this argument. let mut any_same_name = false; let mut any_different_type = false; let mut any_different = false; let arg_name = signature.orig.args[i].name; for other in actual_signatures.iter() { if other.orig.args.get(i).map(|s| s.name) == Some(arg_name) { if !ptr::eq(signature, other) { any_same_name = true; } } if let Some(other) = other.args.get(i) { if other != arg { any_different_type = true; any_different = true; } } else { any_different = true; } } // If all signatures have the exact same type for this argument, // then there's nothing to disambiguate so we don't modify the // name. if !any_different { continue; } if first { rust_name.push_str("_with_"); first = false; } else { rust_name.push_str("_and_"); } // If this name of the argument for this signature is unique // then that's a bit more human readable so we include it in the // method name. Otherwise the type name should disambiguate // correctly. // // If any signature's argument has the same name as our argument // then we can't use that if the types are also the same because // otherwise it could be ambiguous. if any_same_name && any_different_type { arg.push_snake_case_name(&mut rust_name); } else { rust_name.push_str(&snake_case_ident(arg_name)); } } let structural = force_structural || is_structural(signature.orig.attrs.as_ref(), container_attrs); let catch = force_throws || throws(&signature.orig.attrs); let ret_ty = if id == &OperationId::IndexingGetter { // All indexing getters should return optional values (or // otherwise be marked with catch). match ret_ty { IdlType::Nullable(_) => ret_ty, ref ty @ _ => { if catch { ret_ty } else { IdlType::Nullable(Box::new(ty.clone())) } } } } else { ret_ty }; let variadic = signature.args.len() == signature.orig.args.len() && signature .orig .args .last() .map(|arg| arg.variadic) .unwrap_or(false); fn idl_arguments<'a>( args: impl Iterator)>, ) -> Option> { let mut output = vec![]; for (name, idl_type) in args { let ty = match idl_type.to_syn_type(TypePosition::Argument) { Ok(ty) => ty.unwrap(), Err(_) => { return None; } }; output.push((rust_ident(&snake_case_ident(&name[..])), ty)); } Some(output) } let arguments = idl_arguments( signature .args .iter() .zip(&signature.orig.args) .map(|(idl_type, orig_arg)| (orig_arg.name.to_string(), idl_type)), ); // Stable types can have methods that have unstable argument types. // If any of the arguments types are `unstable` then this method is downgraded // to be unstable. let unstable_override = match unstable { // only downgrade stable methods false => signature .orig .args .iter() .any(|arg| is_type_unstable(arg.ty, unstable_types)), true => true, }; if let Some(arguments) = arguments { if let Ok(ret_ty) = ret_ty.to_syn_type(TypePosition::Return) { ret.push(InterfaceMethod { name: rust_ident(&rust_name), js_name: name.to_string(), arguments, ret_ty, kind: kind.clone(), is_static, structural, catch, variadic, unstable: unstable_override, }); } } if !variadic { continue; } let last_idl_type = &signature.args[signature.args.len() - 1]; let last_name = signature.orig.args[signature.args.len() - 1].name; for i in 0..=MAX_VARIADIC_ARGUMENTS_COUNT { let arguments = idl_arguments( signature.args[..signature.args.len() - 1] .iter() .zip(&signature.orig.args) .map(|(idl_type, orig_arg)| (orig_arg.name.to_string(), idl_type)) .chain((1..=i).map(|j| (format!("{}_{}", last_name, j), last_idl_type))), ); if let Some(arguments) = arguments { if let Ok(ret_ty) = ret_ty.to_syn_type(TypePosition::Return) { ret.push(InterfaceMethod { name: rust_ident(&format!("{}_{}", rust_name, i)), js_name: name.to_string(), arguments, kind: kind.clone(), ret_ty, is_static, structural, catch, variadic: false, unstable: unstable_override, }); } } } } return ret; } /// When generating our web_sys APIs we default to setting slice references that /// get passed to JS as mutable in case they get mutated in JS. /// /// In certain cases we know for sure that the slice will not get mutated - for /// example when working with the WebGlRenderingContext APIs. /// /// Here we implement a whitelist for those cases. This whitelist is currently /// maintained by hand. /// /// When adding to this whitelist add tests to crates/web-sys/tests/wasm/whitelisted_immutable_slices.rs fn maybe_adjust<'a>(&self, mut idl_type: IdlType<'a>, id: &'a OperationId) -> IdlType<'a> { let op = match id { OperationId::Operation(Some(op)) => op, OperationId::Constructor(Some(op)) => op, _ => return idl_type, }; if IMMUTABLE_SLICE_WHITELIST.contains(op) { flag_slices_immutable(&mut idl_type) } idl_type } } pub fn is_type_unstable(ty: &weedle::types::Type, unstable_types: &HashSet) -> bool { match ty { weedle::types::Type::Single(SingleType::NonAny(NonAnyType::Identifier(i))) => { // Check if the type in the unstable type list unstable_types.contains(&i.type_) } _ => false, } } /// Search for an attribute by name in some webidl object's attributes. fn has_named_attribute(list: Option<&ExtendedAttributeList>, attribute: &str) -> bool { let list = match list { Some(list) => list, None => return false, }; list.body.list.iter().any(|attr| match attr { ExtendedAttribute::NoArgs(name) => (name.0).0 == attribute, _ => false, }) } fn has_ident_attribute(list: Option<&ExtendedAttributeList>, ident: &str) -> bool { let list = match list { Some(list) => list, None => return false, }; list.body.list.iter().any(|attr| match attr { ExtendedAttribute::Ident(id) => id.lhs_identifier.0 == ident, ExtendedAttribute::IdentList(id) => id.identifier.0 == ident, _ => false, }) } /// ChromeOnly is for things that are only exposed to privileged code in Firefox. pub fn is_chrome_only(ext_attrs: &Option) -> bool { has_named_attribute(ext_attrs.as_ref(), "ChromeOnly") } /// Whether a webidl object is marked as a no interface object. pub fn is_no_interface_object(ext_attrs: &Option) -> bool { has_named_attribute(ext_attrs.as_ref(), "NoInterfaceObject") } pub fn get_rust_deprecated<'a>(ext_attrs: &Option>) -> Option<&'a str> { ext_attrs .as_ref()? .body .list .iter() .filter_map(|attr| match attr { ExtendedAttribute::Ident(id) => Some(id), _ => None, }) .filter(|attr| attr.lhs_identifier.0 == "RustDeprecated") .filter_map(|ident| match ident.rhs { IdentifierOrString::String(s) => Some(s), IdentifierOrString::Identifier(_) => None, }) .next() .map(|s| s.0) } /// Whether a webidl object is marked as structural. pub fn is_structural( item_attrs: Option<&ExtendedAttributeList>, container_attrs: Option<&ExtendedAttributeList>, ) -> bool { // Note that once host bindings is implemented we'll want to switch this // from `true` to `false`, and then we'll want to largely read information // from the WebIDL about whether to use structural bindings or not. true || has_named_attribute(item_attrs, "Unforgeable") || has_named_attribute(container_attrs, "Unforgeable") || has_ident_attribute(container_attrs, "Global") } /// Whether a webidl object is marked as throwing. pub fn throws(attrs: &Option) -> bool { has_named_attribute(attrs.as_ref(), "Throws") } fn flag_slices_immutable(ty: &mut IdlType) { match ty { IdlType::Int8Array { immutable } | IdlType::Uint8Array { immutable } | IdlType::Uint8ClampedArray { immutable } | IdlType::Int16Array { immutable } | IdlType::Uint16Array { immutable } | IdlType::Int32Array { immutable } | IdlType::Uint32Array { immutable } | IdlType::Float32Array { immutable } | IdlType::Float64Array { immutable } | IdlType::ArrayBufferView { immutable } | IdlType::BufferSource { immutable } => *immutable = true, IdlType::Nullable(item) => flag_slices_immutable(item), IdlType::FrozenArray(item) => flag_slices_immutable(item), IdlType::Sequence(item) => flag_slices_immutable(item), IdlType::Promise(item) => flag_slices_immutable(item), IdlType::Record(item1, item2) => { flag_slices_immutable(item1); flag_slices_immutable(item2); } IdlType::Union(list) => { for item in list { flag_slices_immutable(item); } } // catch-all for everything else like Object _ => {} } } pub fn required_doc_string(options: &Options, features: &BTreeSet) -> Option { if !options.features || features.len() == 0 { return None; } let list = features .iter() .map(|ident| format!("`{}`", ident)) .collect::>() .join(", "); Some(format!( "\n\n*This API requires the following crate features \ to be activated: {}*", list, )) } pub fn get_cfg_features(options: &Options, features: &BTreeSet) -> Option { let len = features.len(); if !options.features || len == 0 { None } else { let features = features .into_iter() .map(|feature| quote!( feature = #feature, )) .collect::(); // This is technically unneeded but it generates more idiomatic code if len == 1 { Some(syn::parse_quote!( #[cfg(#features)] )) } else { Some(syn::parse_quote!( #[cfg(all(#features))] )) } } }