Fix polyfill of TextEncoder and TextDecoder

This commit does a few things, including:

* Fixing the generated JS of `wasm-bindgen` to allow polyfills to work.
  (a minor tweak of the generated JS)

* All examples are updated to include a Webpack-specific polyfill for
  these two types to get examples working in Edge.

* A new page has been added to the guide about supported browsers. This
  mentions known caveats like IE 11 requiring `wasm2js` as well as
  documenting some `TextEncoder` and `TextDecoder` workarounds for Edge.

Closes #895
This commit is contained in:
Alex Crichton 2018-09-28 20:01:46 -07:00
parent 0ac84c231a
commit 717cfa303d
52 changed files with 255 additions and 54 deletions

View File

@ -972,7 +972,7 @@ impl<'a> Context<'a> {
"
function passStringToWasm(arg) {{
{}
const buf = cachedEncoder.encode(arg);
const buf = cachedTextEncoder.encode(arg);
const ptr = wasm.__wbindgen_malloc(buf.length);
getUint8Memory().set(buf, ptr);
return [ptr, buf.length];
@ -1065,58 +1065,32 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("text_encoder") {
return;
}
if self.config.nodejs_experimental_modules {
self.imports
.push_str("import { TextEncoder } from 'util';\n");
} else if self.config.nodejs {
self.global(
"
const TextEncoder = require('util').TextEncoder;
",
);
} else if !(self.config.browser || self.config.no_modules) {
self.global(
"
const TextEncoder = typeof self === 'object' && self.TextEncoder
? self.TextEncoder
: require('util').TextEncoder;
",
);
}
self.global(
"
let cachedEncoder = new TextEncoder('utf-8');
",
);
self.expose_text_processor("TextEncoder");
}
fn expose_text_decoder(&mut self) {
if !self.exposed_globals.insert("text_decoder") {
return;
}
self.expose_text_processor("TextDecoder");
}
fn expose_text_processor(&mut self, s: &str) {
if self.config.nodejs_experimental_modules {
self.imports
.push_str("import { TextDecoder } from 'util';\n");
self.imports.push_str(&format!("import {{ {} }} from 'util';\n", s));
self.global(&format!("let cached{0} = new {0}('utf-8');", s));
} else if self.config.nodejs {
self.global(
"
const TextDecoder = require('util').TextDecoder;
",
);
self.global(&format!("const {0} = require('util').{0};", s));
self.global(&format!("let cached{0} = new {0}('utf-8');", s));
} else if !(self.config.browser || self.config.no_modules) {
self.global(
"
const TextDecoder = typeof self === 'object' && self.TextDecoder
? self.TextDecoder
: require('util').TextDecoder;
",
&format!("
const l{0} = typeof {0} === 'undefined' ? \
require('util').{0} : {0};\
", s)
);
self.global(&format!("let cached{0} = new l{0}('utf-8');", s));
}
self.global(
"
let cachedDecoder = new TextDecoder('utf-8');
",
);
}
fn expose_get_string_from_wasm(&mut self) {
@ -1148,7 +1122,7 @@ impl<'a> Context<'a> {
self.global(&format!(
"
function getStringFromWasm(ptr, len) {{
return cachedDecoder.decode(getUint8Memory().{}(ptr, ptr + len));
return cachedTextDecoder.decode(getUint8Memory().{}(ptr, ptr + len));
}}
",
method

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -8,7 +9,13 @@ module.exports = {
filename: 'index.js',
},
plugins: [
new HtmlWebpackPlugin()
new HtmlWebpackPlugin(),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'
};

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

0
examples/canvas/src/lib.rs Executable file → Normal file
View File

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,11 +1,13 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.8.3",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.4"
"webpack-dev-server": "^3.1.0"
}
}

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

0
examples/closures/src/lib.rs Executable file → Normal file
View File

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,8 +1,11 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.0"

0
examples/duck-typed-interfaces/src/lib.rs Executable file → Normal file
View File

View File

@ -1,4 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -6,5 +8,14 @@ module.exports = {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
},
plugins: [
new HtmlWebpackPlugin(),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'
};

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -8,7 +9,13 @@ module.exports = {
filename: 'index.js',
},
plugins: [
new HtmlWebpackPlugin()
new HtmlWebpackPlugin(),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'
};

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

0
examples/guide-supported-types-examples/src/lib.rs Executable file → Normal file
View File

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -8,7 +9,13 @@ module.exports = {
filename: 'index.js',
},
plugins: [
new HtmlWebpackPlugin()
new HtmlWebpackPlugin(),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'
};

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -8,7 +9,13 @@ module.exports = {
filename: 'index.js',
},
plugins: [
new HtmlWebpackPlugin()
new HtmlWebpackPlugin(),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'
};

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,10 +1,12 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.0.1",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.0"
}

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,10 +11,13 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development',
devServer: {
open: true
}
mode: 'development'
};

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

0
examples/paint/src/lib.rs Executable file → Normal file
View File

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

@ -1,10 +1,12 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.16.5",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.0"
}

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

0
examples/webgl/.gitignore vendored Executable file → Normal file
View File

0
examples/webgl/Cargo.toml Executable file → Normal file
View File

0
examples/webgl/README.md Executable file → Normal file
View File

0
examples/webgl/index.html Executable file → Normal file
View File

0
examples/webgl/index.js Executable file → Normal file
View File

2
examples/webgl/package.json Executable file → Normal file
View File

@ -1,8 +1,10 @@
{
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server"
},
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",

0
examples/webgl/src/lib.rs Executable file → Normal file
View File

7
examples/webgl/webpack.config.js Executable file → Normal file
View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: './index.js',
@ -10,6 +11,12 @@ module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
// Have this example work in Edge which doesn't ship `TextEncoder` or
// `TextDecoder` at this time.
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
],
mode: 'development'

View File

View File

@ -38,6 +38,7 @@
- [Command Line Interface](./reference/cli.md)
- [Optimizing for Size](./reference/optimize-size.md)
- [Supported Rust Targets](./reference/rust-targets.md)
- [Supported Browsers](./reference/browser-support.md)
- [Supported Types](./reference/types.md)
- [Imported JavaScript Types](./reference/types/imported-js-types.md)
- [Exported Rust Types](./reference/types/exported-rust-types.md)

View File

@ -0,0 +1,61 @@
# Supported Browsers
The output of `wasm-bindgn` includes a JS file, and as a result it's good to
know what browsers that file is expected to be used in! By default the output
uses ES modules which isn't implemented in all browsers today, but when using a
bundler (like Webpack) you should be able to produce output suitable for all
browsers.
Firefox, Chrome, Safari, and Edge browsers are all supported by
`wasm-bindgen`. If you find a problem in one of these browsers please [report
it] as we'd like to fix the bug! If you find a bug in another browser we would
also like to be aware of it!
## Caveats
* **IE 11** - `wasm-bindgen` by default requires support for
`WebAssembly`, but no version of IE currently supports `WebAssembly`. You can
support IE by [compiling wasm files to JS using `wasm2js`][w2js] (you can [see
an example of doing this too](../examples/wasm2js.html)). Note
that at this time no bundler will do this by default, but we'd love to
document plugins which do this if you are aware of one!
* **Edge** - the `TextEncoder` and `TextDecoder` APIs are not currently
available in Edge which `wasm-bindgen` uses to encode/decode strings between
JS and Rust. You can polyfill this with at least one of two strategies:
1. If using a bundler, you can likely configure the bundler to polyfill these
types by default. For example if you're using Webpack you can use the
[`ProvidePlugin` interface][wpp] like so after also adding
[`text-encoding`] to your `package.json`
```js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.ProvidePlugin({
TextDecoder: ['text-encoding', 'TextDecoder'],
TextEncoder: ['text-encoding', 'TextEncoder']
})
]
// ... other configuration options
};
```
2. If you're not using a bundler you can also include support manually by
adding a `<script>` tag which defines the `TextEncoder` and `TextDecoder`
globals. [This StackOverflow question][soq] has some example usage and MDN
has a [`TextEncoder` polyfill implementation][mdntepi] to get you started
as well.
If you find other incompatibilities please report them to us! We'd love to
either keep this list up-to-date or fix the underlying bugs :)
[report it]: https://github.com/rustwasm/wasm-bindgen/issues/new
[w2js]: https://github.com/WebAssembly/binaryen
[wpp]: https://webpack.js.org/plugins/provide-plugin/
[`text-encoding`]: https://www.npmjs.com/package/text-encoding
[soq]: https://stackoverflow.com/questions/40662142/polyfill-for-textdecoder/46549188#46549188
[mdntepi]: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder#Polyfill

View File

@ -1,6 +1,7 @@
{
"license": "MIT",
"devDependencies": {
"text-encoding": "^0.7.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.17.1",
"webpack-cli": "^3.1.1",