├── .gitignore ├── book.toml ├── generate-book.sh ├── .travis.yml ├── LICENSE-MIT ├── README.md ├── 000-template.md ├── text ├── 008-npm-dependencies.md ├── 001-the-rfc-process.md ├── 005-structural-and-deref.md ├── 006-local-js-dependencies.md ├── 002-wasm-bindgen-inheritance-casting.md └── 007-2019-roadmap.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | book/ 3 | src/ 4 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rust and WebAssembly RFCs" 3 | author = "The Rust and WebAssembly Developers" 4 | description = "A collection of the accepted RFCs for Rust and WebAssembly" 5 | 6 | [output.html] 7 | no-section-label = true 8 | -------------------------------------------------------------------------------- /generate-book.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | cd $(dirname $0) 6 | 7 | if [ ! -d src ]; then 8 | mkdir src 9 | fi 10 | 11 | echo "[Introduction](introduction.md)\n" > src/SUMMARY.md 12 | 13 | for f in $(ls text | sort) 14 | do 15 | echo "- [$(basename $f ".md")]($f)" >> src/SUMMARY.md 16 | cp text/$f src 17 | done 18 | 19 | cp README.md src/introduction.md 20 | 21 | mdbook build 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | cache: 5 | - cargo 6 | 7 | rust: 8 | - stable 9 | 10 | before_script: 11 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 12 | - (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.1" mdbook) 13 | - cargo install-update -a 14 | 15 | script: 16 | - ./generate-book.sh 17 | 18 | deploy: 19 | provider: pages 20 | skip-cleanup: true 21 | github-token: $GITHUB_TOKEN 22 | local-dir: book 23 | keep-history: false 24 | on: 25 | branch: master 26 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust and WebAssembly RFCs 2 | [Rust RFCs]: #rust-rfcs 3 | 4 | Many changes, including bug fixes and documentation improvements can be 5 | implemented and reviewed via the normal GitHub pull request workflow. 6 | 7 | Some changes though are "substantial", and we ask that these be put through a 8 | bit of a design process and produce a consensus among the Rust and WebAssembly 9 | community. 10 | 11 | The "RFC" (request for comments) process is intended to provide a consistent and 12 | controlled path for substantial changes and additions to enter the Rust and 13 | WebAssembly ecosystem, so that all stakeholders can be confident about the 14 | direction the ecosystem is evolving in. 15 | 16 | ## The RFC Process 17 | 18 | When does a change require an RFC? How does an RFC get approved or rejected? 19 | What is the RFC life cycle? 20 | 21 | [These questions are answered in RFC 001][rfc-001]. 22 | 23 | [rfc-001]: https://rustwasm.github.io/rfcs/001-the-rfc-process.html 24 | 25 | ## License 26 | [License]: #license 27 | 28 | This repository is currently in the process of being licensed under either of 29 | 30 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 31 | http://www.apache.org/licenses/LICENSE-2.0) 32 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 33 | 34 | at your option. Some parts of the repository are already licensed according to 35 | those terms. For more see [RFC 36 | 2044](https://github.com/rust-lang/rfcs/pull/2044) and its [tracking 37 | issue](https://github.com/rust-lang/rust/issues/43461). 38 | 39 | ### Contributions 40 | 41 | Unless you explicitly state otherwise, any contribution intentionally submitted 42 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 43 | dual licensed as above, without any additional terms or conditions. 44 | -------------------------------------------------------------------------------- /000-template.md: -------------------------------------------------------------------------------- 1 | - Start Date: (fill me in with today's date, YYYY-MM-DD) 2 | - RFC PR: (leave this empty) 3 | - Tracking Issue: (leave this empty) 4 | 5 | # Summary 6 | [summary]: #summary 7 | 8 | One paragraph explanation of the proposal. 9 | 10 | # Motivation 11 | [motivation]: #motivation 12 | 13 | Why are we doing this? What use cases does it support? What problems does it 14 | solve? What is the expected outcome? 15 | 16 | # Stakeholders 17 | [stakeholders]: #stakeholders 18 | 19 | * Who is affected by this RFC? 20 | 21 | * How are we soliciting feedback on this RFC from these stakeholders? Note that 22 | they may not be watching the RFCs repository or even aren't directly active in 23 | the Rust and WebAssembly working group. 24 | 25 | # Detailed Explanation 26 | [detailed-explanation]: #detailed-explanation 27 | 28 | - Introduce and explain new concepts. 29 | 30 | - It should be reasonably clear how the proposal would be implemented. 31 | 32 | - Provide representative examples that show how this proposal would be commonly 33 | used. 34 | 35 | - Corner cases should be dissected by example. 36 | 37 | # Drawbacks 38 | [drawbacks]: #drawbacks 39 | 40 | - Why should we *not* do this? 41 | 42 | # Rationale and Alternatives 43 | [alternatives]: #rationale-and-alternatives 44 | 45 | This is your chance to discuss your proposal in the context of the whole design 46 | space. This is probably the most important section! 47 | 48 | - Why is this design the best in the space of possible designs? 49 | 50 | - What other designs have been considered and what is the rationale for not 51 | choosing them? 52 | 53 | - What is the impact of not doing this? 54 | 55 | # Unresolved Questions 56 | [unresolved]: #unresolved-questions 57 | 58 | - What parts of the design do you expect to resolve through the RFC process 59 | before this gets merged? 60 | 61 | - What parts of the design do you expect to resolve through the implementation 62 | of this feature? 63 | 64 | - What related issues do you consider out of scope for this RFC that could be 65 | addressed in the future independently of the solution that comes out of this 66 | RFC? 67 | -------------------------------------------------------------------------------- /text/008-npm-dependencies.md: -------------------------------------------------------------------------------- 1 | - Start Date: 2018-02-14 2 | - RFC PR: (leave this empty) 3 | - Tracking Issue: (leave this empty) 4 | 5 | # Summary 6 | [summary]: #summary 7 | 8 | Enable Rust crates to transparently depend on packages in the npm ecosystem. 9 | These dependencies will, like normal Rust dependencies through Cargo, work 10 | seamlessly when consumed by other crates. 11 | 12 | # Motivation 13 | [motivation]: #motivation 14 | 15 | The primary goal of `wasm-bindgen` and `wasm-pack` is to enable seamless 16 | integration of Rust with JS. A massive portion of the JS ecosystem, npm, however 17 | currently has little support in `wasm-bindgen` and `wasm-pack`, making it 18 | difficult to access this rich resource that JS offers! 19 | 20 | The goal of this RFC is to enable these dependencies to exist. Rust crates 21 | should be able to require functionality from NPM, just like how NPM can require 22 | Rust crates compiled to wasm. Any workflow which currently uses NPM packages 23 | (such as packaging WebAssembly with a bundler) should continue to work but also 24 | allow pulling in "custom" NPM packages as well as requested by Rust 25 | dependencies. 26 | 27 | # Stakeholders 28 | [stakeholders]: #stakeholders 29 | 30 | This RFC primarily affects uses of `wasm-pack` and `wasm-bindgen` who are also 31 | currently using bundlers like Webpack. This also affects, however, developers of 32 | core foundational crates in the Rust ecosystem who want to be concious of the 33 | ability to pull in NPM dependencies and such. 34 | 35 | # Detailed Explanation 36 | [detailed-explanation]: #detailed-explanation 37 | 38 | Adding an NPM dependency to a Rust project will look very similar to adding an 39 | NPM dependency to a normal JS project. First the dependency, and its version 40 | requirement, need to be declare. This RFC proposes doing this in a 41 | `package.json` file adjacent to the crate's `Cargo.toml` file: 42 | 43 | ```json 44 | { 45 | "dependencies": { 46 | "foo": "^1.0.1" 47 | } 48 | } 49 | ``` 50 | 51 | The `package.json` file will initially be a subset of NPM's `package.json`, 52 | only supporting one `dependencies` top-level key which internally has key/value 53 | pairs with strings. Beyond this validation though no validation will be 54 | performed of either key or value pairs within `dependencies`. In the future 55 | it's intended that more keys of `package.json` in NPM will be supported, but 56 | this RFC is intended to be an MVP for now to enable dependencies on NPM at all. 57 | 58 | After this `package.json` file is created, the package next needs to be 59 | imported in the Rust crate. Like with other Rust dependencies on JS, this will 60 | be done with the `#[wasm_bindgen]` attribute: 61 | 62 | ```rust 63 | #[wasm_bindgen(module = "foo")] 64 | extern "C" { 65 | fn function_in_foo_package(); 66 | } 67 | ``` 68 | 69 | > **Note**: in JS the above import would be similar to: 70 | > 71 | > ```js 72 | > import { function_in_foo_package } from "foo"; 73 | > ``` 74 | 75 | The exiting `module` key in the `#[wasm_bindgen]` attribute can be used to 76 | indicate which ES module the import is coming from. This affects the `module` 77 | key in the final output wasm binary, and corresponds to the name of the package 78 | in `package.json`. This is intended to match how bundler conventions already 79 | interpret NPM packages as ES modules. 80 | 81 | After these two tools are in place, all that's needed is a `wasm-pack build` and 82 | you should be good to go! The final `package.json` will have the `foo` 83 | dependency listed in our `package.json` above and be ready for consumption via a 84 | bundler. 85 | 86 | ### Technical Implementation 87 | 88 | Under the hood there's a few moving parts which enables all of this to happen. 89 | Let's first take a look at the pieces in `wasm-bindgen`. 90 | 91 | The primary goal of this RFC is to enable *tranparent* and *transitive* 92 | dependencies on NPM. The `#[wasm_bindgen]` macro is the only aspect of a crate's 93 | build which has access to all transitive dependencies, so this is what we'll be 94 | using to slurp up `package.json`. When `#[wasm_bindgen]` with a `module` key is 95 | specified it will look for `package.json` inside the cwd of the procedural macro 96 | (note that the cwd is set by Cargo to be the directory with the crate's 97 | `Cargo.toml` that is being compiled, or the crate in which `#[wasm_bindgen]` is 98 | written). This `package.json`, if found, will have an absolute path to it 99 | encoded into the custom section that `wasm-bindgen` already emits. 100 | 101 | Later, when the `wasm-bindgen` CLI tool executes, it will parse an interpret all 102 | items in the wasm-bindgen custom section. All `package.json` files listed will 103 | be loaded, parsed, and validated (aka only `dependencies` allowed for now). If 104 | any `package.json` is loaded then a `package.json` file will be emitted next to 105 | the output JS file inside of `--out-dir`. 106 | 107 | After `wasm-bindgen` executes, then `wasm-pack` will read the `package.json` 108 | output, if any, and augment it with metadata and other items which are already 109 | emitted. 110 | 111 | If more than one crate in a dependency graph depends on an NPM package then in 112 | this MVP proposal an error will be generated. In the future we can implement 113 | some degree of merging version requirements, but for now to remain simple 114 | `wasm-bindgen` will emit an error. 115 | 116 | ### Interaction with `--no-modules` 117 | 118 | Depending on NPM packages fundamentally requires, well, NPM, in one way or 119 | another. The `wasm-bindgen` and `wasm-pack` CLI tools have modes of output 120 | (notably `wasm-bindgen`'s `--no-modules` and `wasm-pack`'s `--target no-modules` 121 | flags) which are intended to not require NPM and other JS tooling. In these 122 | situations if a `package.json` in any Rust crate is detected an error will be 123 | emitted indicating so. 124 | 125 | Note that this means that core crates which are intended to work with 126 | `--no-modules` will not be able add NPM dependencies. Instead they'll have to 127 | either import Rust dependencies from crates.io or use a feature like [local JS 128 | snippets][js] to import custom JS code. 129 | 130 | [js]: https://github.com/rustwasm/rfcs/pull/6 131 | 132 | # Drawbacks 133 | [drawbacks]: #drawbacks 134 | 135 | One of the primary drawbacks of this RFC is that it's fundamentally incompatible 136 | with a major use case of `wasm-bindgen` and `wasm-pack`, the `--no-modules` and 137 | `--target no-modules` flags. As a short-term band-aid this RFC proposes making 138 | it a hard error which would hinder the adoption of this feature in crates that 139 | want to be usable in this mode. 140 | 141 | In the long-term, however, it may be possible to get this working. For example 142 | many NPM packages are available on `unpkg.com` or in other locations. It may be 143 | possible, if all packages in these locations adhere to well-known conventions, 144 | to generate code that's compatible with these locations of hosting NPM packages. 145 | In these situations it may then be possible to "just drop a script tag" in a few 146 | locations to get `--no-modules` working with NPM packages. It's unclear how 147 | viable this is, though. 148 | 149 | # Rationale and Alternatives 150 | [alternatives]: #rationale-and-alternatives 151 | 152 | When developing this RFC, some guiding values for its design have been 153 | articulated: 154 | 155 | - Development on Rust-generated WebAssembly projects should allow developers to 156 | use the development environment they are most comfortable with. Developers 157 | writing Rust should get to use Rust, and developers using JavaScript should 158 | get to use a JS based runtime environment (Node.js, Chakra, etc). 159 | 160 | - JavaScript tooling and workflows should be usable with Rust-generated 161 | WebAssembly projects. For example, bundlers like WebPack and Parcel, or 162 | dependency management tools such as `npm audit` and GreenKeeper. 163 | 164 | - When possible, decisions should be made that allow the solution to be 165 | available to developers of not just Rust, but also C, and C++. 166 | 167 | - Decisions should be focused on creating workflows that allow developers an 168 | easy learning curve and productive development experience. 169 | 170 | These principles lead to the above proposal of using `package.json` to declare 171 | NPM dependencies which is then grouped together by `wasm-bindgen` to be 172 | published by `wasm-pack`. By using `package.json` we get inherent compatibility 173 | with existing workflows like GreenKeeper and `npm install`. Additionally 174 | `package.json` is very well documented and supported throughout the JS ecosystem 175 | making it very familiar. 176 | 177 | Some other alternatives to this RFC which have been ruled out are: 178 | 179 | * **Using `Cargo.toml` instead of `package.json`** to declare NPM dependencies. 180 | For example we could use: 181 | 182 | ```toml 183 | [package.metadata.npm.dependencies] 184 | foo = "0.1" 185 | ``` 186 | 187 | This has the drawback though of being incompatible with all existing workflows 188 | around `package.json`. Additionally it also highlights a discrepancy between 189 | NPM and Cargo and how `"0.1"` as a version requirement is interpreted (e.g. 190 | `^0.1` or `~0.1`). 191 | 192 | * **Adding a separate manifest file** instead of using `package.json` is also 193 | possibility and might be easier for `wasm-bindgen` to read and later 194 | parse/include. This has a possible benefit of being scoped to exactly our use 195 | case and not being misleading by disallowing otherwise-valid fields of 196 | `package.json`. The downside of this approach is the same as `Cargo.toml`, 197 | however, in that it's an unfamiliar format to most and is incompatible with 198 | existing tooling without bringing too much benefit. 199 | 200 | * **Annotating version dependencies inline** could be used rather than 201 | `package.json` as well, such as: 202 | 203 | ```rust 204 | #[wasm_bindgen(module = "foo", version = "0.1")] 205 | extern "C" { 206 | // ... 207 | } 208 | ``` 209 | 210 | As with all other alternatives this is incompatible with existing tooling, but 211 | it's also not aligned with Rust's own mechanism for declaring dependencies 212 | which separates the location for version information and the code iteslf. 213 | 214 | # Unresolved Questions 215 | [unresolved]: #unresolved-questions 216 | 217 | * Is the MVP restriction of only using `dependencies` too limiting? Should more 218 | fields be supported in `package.json`? 219 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /text/001-the-rfc-process.md: -------------------------------------------------------------------------------- 1 | - Start Date: 2018-06-28 2 | - RFC PR: (leave this empty) 3 | - Tracking Issue: (leave this empty) 4 | 5 | # Summary 6 | [summary]: #summary 7 | 8 | Adopt a simplified version of the Rust RFC process to the Rust and WebAssembly 9 | domain working group. The RFC process will provide a single place to decide on 10 | substantial changes and additions across the ecosystem for all stakeholders. 11 | 12 | # Motivation 13 | [motivation]: #motivation 14 | 15 | There are some decisions which have broad impact across the Rust and WebAssembly 16 | ecosystem, and therefore have many stakeholders who deserve to have a say in the 17 | decision and provide feedback on proposals and designs. Right now, these 18 | decisions tend to be made in whatever local repository pull request or issue 19 | tracker. This makes it difficult for stakeholders to stay on top of these 20 | decisions, because they need to watch many different places. For a repository 21 | owner or team, it is also difficult to determine whether the ecosystem is in 22 | favor of a feature or not. 23 | 24 | After adopting this RFC process, stakeholders should have an easier time staying 25 | on top of substantial changes and features within the ecosystem. Additionally, 26 | the maintainers of a particular repository within the Rust and WebAssembly 27 | ecosystem should feel confident that they've solicited feedback from everyone 28 | involved after going through an RFC, and won't get angry bug reports from users 29 | who felt that they were not consulted. Everyone should have shared confidence in 30 | the direction that the ecosystem evolves in. 31 | 32 | # Detailed Explanation 33 | [detailed-explanation]: #detailed-explanation 34 | 35 | Right now, governance for repositories within the `rustwasm` organization 36 | [follow these rules][repo-governance] describing policy for merging pull 37 | requests: 38 | 39 | > Unless otherwise noted, each `rustwasm/*` repository has the following general 40 | > policies: 41 | > 42 | > * All pull requests must be reviewed and approved of by at least one relevant 43 | > team member or repository collaborator before merging. 44 | > 45 | > * Larger, more nuanced decisions about design, architecture, breaking changes, 46 | > trade offs, etc are made by the relevant team and/or repository 47 | > collaborators consensus. In other words, decisions on things that aren't 48 | > straightforward improvements to or bug fixes for things that already exist 49 | > in the project. 50 | 51 | This policy categorizes pull requests as either "larger, more nuanced ..." 52 | changes or not (we will use "substantial" from now on). When a change is not 53 | substantial, it requires only a single team member approve of it. When a change 54 | is larger and more substantial, then the relevant team comes to consensus on how 55 | to proceed. 56 | 57 | This RFC intends to further sub-categorize substantial changes into those that 58 | affect only maintenance of the repository itself, and are therefore only 59 | substantial *internally* to the maintainers, versus those substantial changes 60 | that have an impact on *external* users and the larger Rust and WebAssembly 61 | community. For internally substantial changes, we do not intend to change the 62 | current policy at all. For substantial changes that have external impact, we 63 | will adopt a lightweight version of Rust's RFC process. 64 | 65 | ## When does a change need an RFC? 66 | 67 | You need to follow the RFC process if you intend to make externally substantial 68 | changes to any repository within the [`rustwasm` organization][org], or the RFC 69 | process itself. What constitutes a "substantial" change is evolving based on 70 | community norms and varies depending on what part of the ecosystem you are 71 | proposing to change, but may include the following: 72 | 73 | - The removal of or breaking changes to public APIs in widespread use. 74 | - Public API additions that extend the public API in new ways (i.e. more than 75 | "we implement `SomeTrait` for `ThisThing`, so also implement `SomeTrait` for 76 | `RelatedThing`"). 77 | 78 | Some changes do not require an RFC: 79 | 80 | - Rephrasing, reorganizing, refactoring, or otherwise "changing shape does 81 | not change meaning". 82 | - Additions that strictly improve objective, numerical quality criteria 83 | (warning removal, speedup, better platform coverage, more parallelism, trap 84 | more errors, etc.) 85 | - Additions only likely to be _noticed by_ other maintainers, and remain 86 | invisible to users. 87 | 88 | If you submit a pull request to implement a new feature without going through 89 | the RFC process, it may be closed with a polite request to submit an RFC first. 90 | 91 | ## The RFC process step by step 92 | 93 | - Fork the [RFC repository][rfc-repo]. 94 | - Copy `000-template.md` to `text/000-my-feature.md` (where "my-feature" is 95 | descriptive. Don't assign an RFC number yet). 96 | - Fill in the RFC. Put care into the details: RFCs that do not present 97 | convincing motivation, demonstrate understanding of the impact of the design, 98 | or are disingenuous about the drawbacks or alternatives tend to be 99 | poorly-received. 100 | - Submit a pull request. As a pull request, the RFC will receive design feedback 101 | from the larger community, and the author should be prepared to revise it in 102 | response. 103 | - Each new RFC pull request will be triaged in the next Rust and WebAssembly 104 | domain working group meeting and assigned to one or more of the [`@rustwasm/*` 105 | teams][teams]. 106 | - Build consensus and integrate feedback. RFCs that have broad support are 107 | much more likely to make progress than those that don't receive any 108 | comments. Feel free to reach out to the RFC assignee in particular to get 109 | help identifying stakeholders and obstacles. 110 | - The team(s) will discuss the RFC pull request, as much as possible in the 111 | comment thread of the pull request itself. Offline discussion will be 112 | summarized on the pull request comment thread. 113 | - RFCs rarely go through this process unchanged, especially as alternatives 114 | and drawbacks are shown. You can make edits, big and small, to the RFC to 115 | clarify or change the design, but make changes as new commits to the pull 116 | request, and leave a comment on the pull request explaining your changes. 117 | Specifically, do not squash or rebase commits after they are visible on the 118 | pull request. 119 | - At some point, a member of the subteam will propose a "motion for final 120 | comment period" (FCP), along with a *disposition* for the RFC (merge, close, 121 | or postpone). 122 | - This step is taken when enough of the tradeoffs have been discussed that the 123 | team(s) are in a position to make a decision. That does not require 124 | consensus amongst all participants in the RFC thread (which may be 125 | impossible). However, the argument supporting the disposition on the RFC 126 | needs to have already been clearly articulated, and there should not be a 127 | strong consensus *against* that position outside of the team(s). Team 128 | members use their best judgment in taking this step, and the FCP itself 129 | ensures there is ample time and notification for stakeholders to push back 130 | if it is made prematurely. 131 | - For RFCs with lengthy discussion, the motion to FCP should be preceded by a 132 | *summary comment* trying to lay out the current state of the discussion and 133 | major tradeoffs/points of disagreement. 134 | - Before actually entering FCP, *all* members of the team(s) must sign off; 135 | this is often the point at which many team members first review the RFC in 136 | full depth. 137 | - The FCP lasts seven calendar days. It is also advertised widely, e.g. in an 138 | issue of ["This Week in Rust and WebAssembly" on the Rust and WebAssembly 139 | blog](https://rustwasm.github.io/). This way all stakeholders have a chance to 140 | lodge any final objections before a decision is reached. 141 | - In most cases, the FCP period is quiet, and the RFC is either merged or 142 | closed. However, sometimes substantial new arguments or ideas are raised, 143 | the FCP is canceled, and the RFC goes back into development mode. 144 | 145 | ## From RFC to implementation 146 | 147 | Once an RFC is merged it becomes "active" then authors may implement it and 148 | submit the feature as a pull request to the relevant repositories. Being 149 | "active" is not a rubber stamp, and in particular still does not mean the 150 | feature will ultimately be merged; it does mean that in principle all the major 151 | stakeholders have agreed to the feature and are amenable to merging it. 152 | 153 | Furthermore, the fact that a given RFC has been accepted and is "active" implies 154 | nothing about what priority is assigned to its implementation, nor does it imply 155 | anything about whether a developer has been assigned the task of implementing 156 | the feature. While it is not *necessary* that the author of the RFC also write 157 | the implementation, it is by far the most effective way to see an RFC through to 158 | completion: authors should not expect that other project developers will take on 159 | responsibility for implementing their accepted feature. 160 | 161 | Modifications to "active" RFCs can be done in follow-up pull requests. We strive 162 | to write each RFC in a manner that it will reflect the final design of the 163 | feature; but the nature of the process means that we cannot expect every merged 164 | RFC to actually reflect what the end result will be at the time of the next 165 | major release. 166 | 167 | In general, once accepted, RFCs should not be substantially changed. Only very 168 | minor changes should be submitted as amendments. More substantial changes should 169 | be new RFCs, with a note added to the original RFC. 170 | 171 | # Rationale and Alternatives 172 | [alternatives]: #rationale-and-alternatives 173 | 174 | The design space for decision making is very large, from democratic to 175 | autocratic and more. 176 | 177 | Forking and simplifying Rust's RFC process is *practical*. Rather than designing 178 | a decision making process from scratch, we take an existing one that works well 179 | and tailor it to our needs. Many Rust and WebAssembly stakeholders are already 180 | familiar with it. 181 | 182 | The main differences from the Rust RFC process are: 183 | 184 | - FCP lasts seven calendar days rather than ten. This reflects our desire for a 185 | lighter-weight process that moves more quickly than Rust's RFC process. 186 | - The RFC template is shorter and merges together into single sections what were 187 | distinct sections in the Rust RFC template. Again, this reflects our desire 188 | for a lighter-weight process where we do not need to go into quite as much 189 | painstaking detail as Rust RFCs sometimes do (perhaps excluding *this* RFC). 190 | 191 | The phases of RFC development and post-RFC implementation are largely the same 192 | as the Rust RFC process. We found that the motivations for nearly every phase of 193 | Rust's RFC process are equally motivating for the Rust and WebAssembly 194 | domain. We expected to simplify phases a lot, for example, we initially 195 | considered removing FCP and going straight to signing off on accepting an RFC or 196 | not. However, FCP exists as a way to (1) allow stakeholders to voice any final 197 | concerns that hadn't been brought up yet, and (2) help enforce the "no new 198 | rationale" rule. Both points apply equally well to the Rust and WebAssembly 199 | domain working group and ecosystem as they apply to Rust itself. 200 | 201 | We can also avoid adopting an RFC process, and move more quickly by allowing 202 | each repository's team or owner to be dictators of their corner of the 203 | ecosystem. However, this will result in valuable feedback, opinions, and insight 204 | not getting voiced, and narrow decisions being made. 205 | 206 | # Unresolved Questions 207 | [unresolved]: #unresolved-questions 208 | 209 | - Will we use [`@rfcbot`][rfcbot]? If we can, we probably should, but this can 210 | be decided separately from whether to accept this RFC. 211 | 212 | - How to best advertise new RFCs and FCP? Should we make "This Week in Rust and 213 | WebAssembly" actually be weekly rather than every other week? The interaction 214 | between FCP length and frequency of TWiRaWA posting seems important. 215 | 216 | [rfcbot]: https://github.com/anp/rfcbot-rs 217 | [teams]: https://github.com/rustwasm/team/blob/master/GOVERNANCE.md#teams 218 | [org]: https://github.com/rustwasm 219 | [rfc-repo]: http://github.com/rustwasm/rfcs 220 | [repo-governance]: https://github.com/rustwasm/team/blob/master/GOVERNANCE.md#repositories 221 | -------------------------------------------------------------------------------- /text/005-structural-and-deref.md: -------------------------------------------------------------------------------- 1 | - Start Date: 2018-10-05 2 | - RFC PR: https://github.com/rustwasm/rfcs/pull/5 3 | - Tracking Issue: (leave this empty) 4 | 5 | # Summary 6 | [summary]: #summary 7 | 8 | Change `#[wasm_bindgen]` to use `structural` by default, and add a new 9 | attribute `final` for an opt-in to today's behavior. Once implemented then use 10 | `Deref` to model the class inheritance hierarchy in `web-sys` and `js-sys` to 11 | enable ergonomic usage of superclass methods of web types. 12 | 13 | # Motivation 14 | [motivation]: #motivation 15 | 16 | The initial motivation for this is outlined [RFC 3], namely that the `web-sys` 17 | crate provides bindings for many APIs found on the web but accessing the 18 | functionality of parent classes is quite cumbersome. 19 | 20 | The web makes extensive use of class inheritance hierarchies, and in `web-sys` 21 | right now each class gets its own `struct` type with inherent methods. These 22 | types implement `AsRef` between one another for subclass relationships, but it's 23 | quite unergonomic to actually reference the functionality! For example: 24 | 25 | ```rust 26 | let x: &Element = ...; 27 | let y: &Node = x.as_ref(); 28 | y.append_child(...); 29 | ``` 30 | 31 | or... 32 | 33 | ```rust 34 | let x: &Element = ...; 35 | >::as_ref(x) 36 | .append_child(...); 37 | ``` 38 | 39 | It's be much nicer if we could support this in a more first-class fashion and 40 | make it more ergonomic! 41 | 42 | [RFC 3]: https://github.com/rustwasm/rfcs/pull/3 43 | 44 | > **Note**: While this RFC has the same motivation as [RFC 3] it's proposing an 45 | > alternative solution, specifically enabled by switching by `structural` by 46 | > default, which is discussed in [RFC 3] but is hopefully formally outlined 47 | > here. 48 | 49 | # Detailed Explanation 50 | [detailed-explanation]: #detailed-explanation 51 | 52 | This RFC proposes using the built-in `Deref` trait to model the class hierarchy 53 | found on the web in `web-sys`. This also proposes changes to `#[wasm_bindgen]` 54 | to make using `Deref` feasible for binding arbitrary JS apis (such as those on 55 | NPM) with `Deref` as well. 56 | 57 | For example, `web-sys` will contain: 58 | 59 | ```rust 60 | impl Deref for Element { 61 | type Target = Node; 62 | 63 | fn deref(&self) -> &Node { /* ... */ } 64 | } 65 | ``` 66 | 67 | allowing us to write our example above as: 68 | 69 | ```rust 70 | let x: &Element = ...; 71 | x.append_child(...); // implicit deref to `Node`! 72 | ``` 73 | 74 | All JS types in `web-sys` and in general have at most one superclass. Currently, 75 | however, the `#[wasm_bindgen]` attribute allows specifying multiple `extends` 76 | attributes to indicate superclasses: 77 | 78 | ```rust 79 | #[wasm_bindgen] 80 | extern { 81 | #[wasm_bindgen(extends = Node, extends = Object)] 82 | type Element; 83 | 84 | // ... 85 | } 86 | ``` 87 | 88 | The `web-sys` API generator currently lists an `extends` for all superclasses, 89 | transitively. This is then used in the code generator to generate `AsRef` 90 | implementatiosn for `Element`. 91 | 92 | The code generation of `#[wasm_bindgen]` will be updated with the following 93 | rules: 94 | 95 | * If no `extends` attribute is present, defined types will implement 96 | `Deref`. 97 | * Otherwise, the *first* `extends` attribute is used to implement 98 | `Deref`. 99 | * (long term, currently require a breaking change) reject multiple `extends` 100 | attributes, requiring there's only one. 101 | 102 | This means that `web-sys` may need to be updated to ensure that the immediate 103 | superclass is listed first in `extends`. Manual bindings will continue to work 104 | and will have the old `AsRef` implementations as well as a new `Deref` 105 | implementation. 106 | 107 | The `Deref` implementation will concretely be implemented as: 108 | 109 | ```rust 110 | impl Deref for #imported_type { 111 | type Target = #target_type; 112 | 113 | #[inline] 114 | fn deref(&self) -> &#target_type { 115 | ::wasm_bindgen::JsCast::unchecked_ref(self) 116 | } 117 | } 118 | ``` 119 | 120 | ### Switching to `structural` by default 121 | 122 | If we were to implement the above `Deref` proposal as-is today in 123 | `wasm-bindgen`, it would have a crucial drawback. It may not handle inheritance 124 | correctly! Let's explore this with an example. Say we have some JS we'd like to 125 | import: 126 | 127 | ```js 128 | class Parent { 129 | constructor() {} 130 | method() { console.log('parent'); } 131 | } 132 | 133 | class Child extends Parent { 134 | constructor() {} 135 | method() { console.log('child'); } 136 | } 137 | ``` 138 | 139 | we would then bind this in Rust with: 140 | 141 | ```rust 142 | #[wasm_bindgen] 143 | extern { 144 | type Parent; 145 | #[wasm_bindgen(constructor)] 146 | fn new() -> Parent; 147 | #[wasm_bindgen(method)] 148 | fn method(this: &Parent); 149 | 150 | #[wasm_bindgen(extends = Parent)] 151 | type Child; 152 | #[wasm_bindgen(constructor)] 153 | fn new() -> Child; 154 | #[wasm_bindgen(method)] 155 | fn method(this: &Child); 156 | } 157 | ``` 158 | 159 | and we could then use it like so: 160 | 161 | ```rust 162 | #[wasm_bindgen] 163 | pub fn run() { 164 | let parent = Parent::new(); 165 | parent.method(); 166 | let child = Child::new(); 167 | child.method(); 168 | } 169 | ``` 170 | 171 | and we would today see `parent` and `child` logged to the console. Ok everything 172 | is working as expected so far! We know we've got `Deref for 173 | Child`, though, so let's say we tweak this example a bit: 174 | 175 | ```rust 176 | #[wasm_bindgen] 177 | pub fn run() { 178 | call_method(&Parent::new()); 179 | call_method(&Child::new()); 180 | } 181 | 182 | fn call_method(object: &Parent) { 183 | object.method(); 184 | } 185 | ``` 186 | 187 | Here we'd naively (and correctly) expect `parent` and `child` to be output like 188 | before, but much to our surprise this actually prints out `parent` twice! 189 | 190 | The issue with this is how `#[wasm_bindgen]` treats method calls today. When you 191 | say: 192 | 193 | ```rust 194 | #[wasm_bindgen(method)] 195 | fn method(this: &Parent); 196 | ``` 197 | 198 | then `wasm-bindgen` (the CLI tool) generates JS that looks like this: 199 | 200 | ```js 201 | const Parent_method_target = Parent.prototype.method; 202 | 203 | export function __wasm_bindgen_Parent_method(obj) { 204 | Parent_method_target.call(getObject(obj)); 205 | } 206 | ``` 207 | 208 | Here we can see that, by default, `wasm-bindgen` is **reaching into the 209 | `prototype` of each class to figure out what method to call**. This in turn 210 | means that when `Parent::method` is called in Rust, it unconditionally uses the 211 | method defined on `Parent` rather than walking the protype chain (that JS 212 | usually does) to find the right `method` method. 213 | 214 | To improve the situation there's a `structural` attribute to wasm-bindgen to fix 215 | this, which when applied like so: 216 | 217 | ```rust 218 | #[wasm_bindgen(method, structural)] 219 | fn method(this: &Parent); 220 | ``` 221 | 222 | means that the following JS code is generated: 223 | 224 | ```js 225 | const Parent_method_target = function() { this.method(); }; 226 | 227 | // ... 228 | ``` 229 | 230 | Here we can see that a JS function shim is generated instead of using the raw 231 | function value in the prototype. This, however, means that our example above 232 | will indeed print `parent` and then `child` because JS is using prototype 233 | lookups to find the `method` method. 234 | 235 | Phew! Ok with all that information, we can see that **if `structural` is omitted 236 | then JS class hierarchies can be subtly incorrect when methods taking parent 237 | classes are passed child classes which override methods**. 238 | 239 | An easy solution to this problem is to simply use `structural` everywhere, so... 240 | let's propose that! Consequently, this RFC proposes changing `#[wasm_bindgen]` 241 | to act as if all bindings are labeled as `structural`. While technically a 242 | breaking change it's believed that we don't have any usage which would actually 243 | run into the breakage here. 244 | 245 | ### Adding `#[wasm_bindgen(final)]` 246 | 247 | Since `structural` is not the default today we don't actually have a name for 248 | the default behavior of `#[wasm_bindgen]` today. This RFC proposes adding a new 249 | attribute to `#[wasm_bindgen]`, `final`, which indicates that it should have 250 | today's behavior. 251 | 252 | When attached to an attribute or method, the `final` attribute indicates that 253 | the method or attribute should be processed through the `prototype` of a class 254 | rather than looked up structurally via the prototype chain. 255 | 256 | You can think of this as "everything today is `final` by default". 257 | 258 | ### Why is it ok to make `structural` the default? 259 | 260 | One pretty reasonable question you might have at this point is "why, if 261 | `structural` is the default today, is it ok to switch?" To answer this, let's 262 | first explore why `final` is the default today! 263 | 264 | From its inception `wasm-bindgen` has been designed with the future [host 265 | bindings] proposal for WebAssembly. The host bindings proposal promises 266 | faster-than-JS DOM access by removing many of the dynamic checks necessary when 267 | calling DOM methods. This proposal, however, is still in relatively early stages 268 | and hasn't been implemented in any browser yet (as far as we know). 269 | 270 | In WebAssembly on the web all imported functions must be plain old JS functions. 271 | They're all currently invoked with `undefined` as the `this` parameter. With 272 | host bindings, however, there's a way to say that an imported function uses the 273 | first argument to the function as the `this` parameter (like `Function.call` in 274 | JS). This in turn brings the promise of *eliminating any shim functions 275 | necessary when calling imported functionality*. 276 | 277 | As an example, today for `#[wasm_bindgen(method)] fn parent(this: &Parent);` we 278 | generate JS that looks like: 279 | 280 | ```rust 281 | #[wasm_bindgen(method)] 282 | fn method(this: &Parent); 283 | ``` 284 | 285 | means that the following JS code is generated: 286 | 287 | ```js 288 | const Parent_method_target = Parent.prototype.method; 289 | 290 | export function __wasm_bindgen_Parent_method(idx) { 291 | Parent_method_target.call(getObject(idx)); 292 | } 293 | ``` 294 | 295 | If we assume for a moment that [`anyref` is implemented][reference-types] we 296 | could instead change this to: 297 | 298 | ```js 299 | const Parent_method_target = Parent.prototype.method; 300 | 301 | export function __wasm_bindgen_Parent_method(obj) { 302 | Parent_method_target.call(obj); 303 | } 304 | ``` 305 | 306 | (note the lack of need for `getObject`). And finally, with [host bindings] we 307 | can say that the wasm module's import of `__wasm_bindgen_Parent_method` uses the 308 | first parameter as `this`, meaning we can transform this to: 309 | 310 | ```js 311 | export const __wasm_bindgen_Parent_method = Parent.prototype.method; 312 | ``` 313 | 314 | and *voila*, no JS function shims necessary! With `structural` we'll still need 315 | a function shim in this future world: 316 | 317 | ```js 318 | export const __wasm_bindgen_Parent_method = function() { this.method(); }; 319 | ``` 320 | 321 | Alright, with some of those basics out of the way, let's get back to 322 | why-`final`-by-default. The promise of [host bindings] is that by eliminating 323 | all these JS function shims necessary we can be faster than we would otherwise 324 | be, providing a feeling that `final` is faster than `structural`. This future, 325 | however, relies on a number of unimplemented features in wasm engines today. 326 | Let's consequently get an idea of what the performance looks like today! 327 | 328 | I've been slowly over time preparing a [microbenchmark suite][bm] for measuring 329 | JS/wasm/wasm-bindgen performance. The interesting one here is the benchmark 330 | "`structural` vs not". If you click "Run test" in a browser after awhile you'll 331 | see two bars show up. The left-hand one is a method call with `final` and the 332 | right-hand one is a method call with `structural`. The results I see on my 333 | computer are: 334 | 335 | * Firefox 62, `structural` is 3% faster 336 | * Firefox 64, `structural` is 3% slower 337 | * Chrome 69, `structural` is 5% slower 338 | * Edge 42, `structural` is 22% slower 339 | * Safari 12, `strutural` is 17% slower 340 | 341 | So it looks like for Firefox/Chrome it's not really making much of a difference 342 | but in Edge/Safari it's much faster to use `final`! It turns out, however, that 343 | we're not optimizing `structural` as much as we can. Let's change our generated 344 | code from: 345 | 346 | ```js 347 | const Parent_method_target = function() { this.method(); }; 348 | 349 | export function __wasm_bindgen_Parent_method(obj) { 350 | Parent_method_target.call(getObject(obj)); 351 | } 352 | ``` 353 | 354 | to... 355 | 356 | ```js 357 | export function __wasm_bindgen_Parent_method(obj) { 358 | getObject(obj).method(); 359 | } 360 | ``` 361 | 362 | (manually editing the JS today) 363 | 364 | and if we rerun the benchmarks (sorry no online demo) we get: 365 | 366 | * Firefox 62, `structural` is 22% faster 367 | * Firefox 64, `structural` is 10% faster 368 | * Chrome 69, `structural` is 0.3% slower 369 | * Edge 42, `structural` is 15% faster 370 | * Safai 12, `structural` is 8% slower 371 | 372 | and these numbers look quite different! There's some strong data here showing 373 | that `final` *is not universally faster today* and is actually almost 374 | universally slower (when we optimize `structural` slightly). 375 | 376 | Ok! That's all basically a very long winded way of saying **`final` was the 377 | historical default because we thought it was faster, but it turns out that in JS 378 | engines today it isn't always faster**. As a result, this RFC proposes that it's 379 | ok to make `structural` the default. 380 | 381 | [host bindings]: https://github.com/WebAssembly/host-bindings 382 | [reference-types]: https://github.com/WebAssembly/reference-types 383 | [bm]: https://alexcrichton.github.io/rust-wasm-benchmark/ 384 | 385 | # Drawbacks 386 | 387 | `Deref` is a somewhat quiet trait with disproportionately large ramifications. 388 | It affects method resolution (the `.` operator) as well as coercions (`&T` to 389 | `&U`). Discovering this in `web-sys` and/or JS apis in the ecosystem isn't 390 | always the easiest thing to do. It's thought, though, that this aspect of 391 | `Deref` won't come up very often when using JS apis in practice. Instead most 392 | APIs will work "as-is" as you might expect in JS in Rust as well, with `Deref` 393 | being an unobtrusive solution for developers to mostly ignore it an just call 394 | methods. 395 | 396 | Additionally `Deref` has the drawback that it's not explicitly designed for 397 | class inheritance hierarchies. For example `*element` produces a `Node`, 398 | `**element` produces an `Object`, etc. This is expected to not really come up 399 | that much in practice, though, and instead automatic coercions will cover almost 400 | all type conversions. 401 | 402 | # Rationale and Alternatives 403 | [alternatives]: #rationale-and-alternatives 404 | 405 | The primary alternative to this design is [RFC 3], using traits to model the 406 | inheritance hierarchy. The pros/cons of that proposal are well listed in [RFC 407 | 3]. 408 | 409 | # Unresolved Questions 410 | [unresolved]: #unresolved-questions 411 | 412 | None right now! 413 | -------------------------------------------------------------------------------- /text/006-local-js-dependencies.md: -------------------------------------------------------------------------------- 1 | - Start Date: 2018-01-08 2 | - RFC PR: (leave this empty) 3 | - Tracking Issue: (leave this empty) 4 | 5 | # Summary 6 | [summary]: #summary 7 | 8 | Add the ability for `#[wasm_bindgen]` to process, load, and handle dependencies 9 | on local JS files. 10 | 11 | * The `module` attribute can now be used to import files explicitly: 12 | 13 | ```rust 14 | #[wasm_bindgen(module = "/js/foo.js")] 15 | extern "C" { 16 | // ... 17 | } 18 | ``` 19 | 20 | * The `inline_js` attribute can now be used to import JS modules inline: 21 | 22 | ```rust 23 | #[wasm_bindgen(inline_js = "export function foo() {}")] 24 | extern "C" { 25 | fn foo(); 26 | } 27 | ``` 28 | 29 | * The `--browser` flag is repurposed to generate an ES module for the browser 30 | and `--no-modules` is deprecated in favor of this flag. 31 | 32 | * The `--nodejs` will not immediately support local JS snippets, but will do so 33 | in the future. 34 | 35 | # Motivation 36 | [motivation]: #motivation 37 | 38 | The goal of `wasm-bindgen` is to enable easy interoperation between Rust and JS. 39 | While it's very easy to write custom Rust code, it's actually pretty difficult 40 | to write custom JS and hook it up with `#[wasm_bindgen]` (see 41 | [rustwasm/wasm-bindgen#224][issue]). The `#[wasm_bindgen]` 42 | attribute currently only supports importing functions from ES modules, but even 43 | then the support is limited and simply assumes that the ES module string exists 44 | in the final application build step. 45 | 46 | [issue]: https://github.com/rustwasm/wasm-bindgen/issues/224 47 | 48 | Currently there is no composable way for a crate to have some auxiliary JS that 49 | it is built with which ends up seamlessly being included into a final built 50 | application. For example the `rand` crate can't easily include local JS (perhaps 51 | to detect what API for randomness it's supposed to use) without imposing strong 52 | requirements on the final artifact. 53 | 54 | Ergonomically support imports from custom JS files also looks to be required by 55 | frameworks like `stdweb` to build a macro like `js!`. This involves generating 56 | snippets of JS at compile time which need to be included into the final bundle, 57 | which is intended to be powered by this new attribute. 58 | 59 | # Stakeholders 60 | [stakeholders]: #stakeholders 61 | 62 | Some major stakeholders in this RFC are: 63 | 64 | * Users of `#[wasm_bindgen]` 65 | * Crate authors wishing to add wasm support to their crate. 66 | * The `stdweb` authors 67 | * Bundler (webpack) and `wasm-bindgen` integration folks. 68 | 69 | Most of the various folks here will be cc'd onto the RFC, and reaching out to 70 | more is always welcome! 71 | 72 | # Detailed Explanation 73 | [detailed-explanation]: #detailed-explanation 74 | 75 | This proposal involves a number of moving pieces, all of which are intended to 76 | work in concert to provide a streamlined story to including local JS files into 77 | a final `#[wasm_bindgen]` artifact. We'll take a look at each piece at a time 78 | here. 79 | 80 | ### New Syntactical Features 81 | 82 | The most user-facing change proposed here is the reinterpretation of the 83 | `module` attribute inside of `#[wasm_bindgen]` and the addition of an 84 | `inline_js` attribute. They can now be used to import local files and define 85 | local imports like so: 86 | 87 | ```rust 88 | #[wasm_bindgen(module = "/js/foo.js")] 89 | extern "C" { 90 | // ... definitions 91 | } 92 | 93 | #[wasm_bindgen(inline_js = "export function foo() {}")] 94 | extern "C" { 95 | fn foo(); 96 | } 97 | ``` 98 | 99 | The first declaration says that the block of functions and types and such are 100 | all imported from the `/js/foo.js` file, relative to the current file and rooted 101 | at the crate root. The second declaration lists the JS inline as a string 102 | literal and the `extern` block describes the exports of the inline module. 103 | 104 | The following rules are proposed for interpreting a `module` attribute. 105 | 106 | * If the strings starts with the platform-specific representation of an absolute 107 | path to the cargo build directory (identified by `$OUT_DIR`) then the string 108 | is interpreted as a file path in the output directory. This is intended for 109 | build scripts which generate JS files as part of the build. 110 | 111 | * If the string starts with `/`, `./`, or `../` then it's considered a path to a 112 | local file. If not, then it's passed through verbatim as the ES module import. 113 | 114 | * All paths are resolved relative to the current file, like Rust's own 115 | `#[path]`, `include_str!`, etc. At this time, however, it's unknown how we'd 116 | actually do this for relative files. As a result all paths will be required to 117 | start with `/`. When `proc_macro` has a stable API (or we otherwise figure 118 | out how) we can start allowing `./` and `../`-prefixed paths. 119 | 120 | This will hopefully roughly match what programmers expect as well as preexisting 121 | conventions in browsers and bundlers. 122 | 123 | The `inline_js` attribute isn't really intended to be used for general-purpose 124 | development, but rather a way for procedural macros which can't currently today 125 | rely on the presence of `$OUT_DIR` to generate JS to import. 126 | 127 | ### Format of imported JS 128 | 129 | All imported JS is required to written with ES module syntax. Initially the JS 130 | must be hand-written and cannot be postprocessed. For example the JS cannot be 131 | written with TypeScript, nor can it be compiled by Babel or similar. 132 | 133 | As an example, a library may contain: 134 | 135 | ```rust 136 | // src/lib.rs 137 | #[wasm_bindgen(module = "/js/foo.js")] 138 | extern "C" { 139 | fn call_js(); 140 | } 141 | ``` 142 | 143 | accompanied with: 144 | 145 | ```js 146 | // js/foo.js 147 | 148 | export function call_js() { 149 | // ... 150 | } 151 | ``` 152 | 153 | Note that `js/foo.js` uses ES module syntax to export the function `call_js`. 154 | When `call_js` is called from Rust it will call the `call_js` function in 155 | `foo.js`. 156 | 157 | ### Propagation Through Dependencies 158 | 159 | The purpose of the `file` attribute is to work seamlessly with dependencies. 160 | When building a project with `#[wasm_bindgen]` you shouldn't be required to know 161 | whether your dependencies are using local JS snippets or not! 162 | 163 | The `#[wasm_bindgen]` macro, at compile time, will read the contents of the file 164 | provided, if any. This file will be serialized into the wasm-bindgen custom 165 | sections in a wasm-bindgen specific format. The final wasm artifact produced by 166 | rustc will contain all referenced JS file contents in its custom sections. 167 | 168 | The `wasm-bindgen` CLI tool will extract all this JS and write it out to the 169 | filesystem. The wasm file (or the wasm-bindgen-generated shim JS file) emitted 170 | will import all the emitted JS files with relative imports. 171 | 172 | ### Updating `wasm-bindgen` output modes 173 | 174 | The `wasm-bindgen` has a few modes of output generation today. These output 175 | modes are largely centered around modules vs no modules and how modules are 176 | defined. This RFC proposes that we move away from this moreso towards 177 | *environments*, such as node.js-compatible vs browser-compatible code (which 178 | involves more than only module format). This means that in cases where an 179 | environment supports multiple module systems, or the module system is optional 180 | (browsers support es modules and also no modules) `wasm-bindgen` will choose 181 | what module system it thinks is best as long as it is compatible with that 182 | environment. 183 | 184 | The current output modes of `wasm-bindgen` are: 185 | 186 | * **Default** - by default `wasm-bindgen` emits output that assumes the wasm 187 | module itself is an ES module. This will naturally work with custom JS 188 | snippets that are themselves ES modules, as they'll just be more modules in 189 | the graph all found in the local output directory. This output mode is 190 | currently only consumable by bundlers like Webpack, the default output cannot 191 | be loaded in either a web browser or Node.js. 192 | 193 | * **`--no-modules`** - the `--no-modules` flag to `wasm-bindgen` is incompatible 194 | with ES modules because it's intended to be included via a `