diff --git a/Cargo.toml b/Cargo.toml
index b004b39b..5154106a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -48,6 +48,7 @@ wasm-bindgen-test-crate-b = { path = 'tests/crates/b', version = '0.1' }
[workspace]
members = [
+ "benchmarks",
"crates/cli",
"crates/js-sys",
"crates/macro/ui-tests",
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 7dc20ca2..dbd4c449 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -144,15 +144,9 @@ jobs:
steps:
- template: ci/azure-install-rust.yml
- template: ci/azure-install-sccache.yml
+ - template: ci/azure-install-wasm-pack.yml
- script: mv _package.json package.json && npm install && rm package.json
displayName: "run npm install"
- - script: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
- displayName: "install wasm-pack"
- - script: |
- set -ex
- cargo build -p wasm-bindgen-cli
- ln -snf `pwd`/target/debug/wasm-bindgen $HOME/.cargo/bin/wasm-bindgen
- displayName: "install wasm-bindgen for `wasm-pack` to use"
- script: |
for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler`; do
(cd examples/$dir &&
@@ -190,6 +184,19 @@ jobs:
artifactName: examples2
targetPath: '$(Build.ArtifactStagingDirectory)'
+ - job: build_benchmarks
+ displayName: "Build benchmarks"
+ steps:
+ - template: ci/azure-install-rust.yml
+ - template: ci/azure-install-sccache.yml
+ - template: ci/azure-install-wasm-pack.yml
+ - script: wasm-pack build --target web benchmarks
+ displayName: "build benchmarks"
+ - task: PublishPipelineArtifact@0
+ inputs:
+ artifactName: benchmarks
+ targetPath: benchmarks
+
- job: dist_linux
displayName: "Dist Linux binary"
steps:
@@ -292,6 +299,7 @@ jobs:
- dist_windows
- build_examples
- build_raytrace
+ - build_benchmarks
displayName: "Deploy everything"
steps:
- template: ci/azure-install-rust.yml
@@ -315,6 +323,11 @@ jobs:
inputs:
artifactName: examples2
targetPath: gh-pages/exbuild/raytrace-parallel
+ - task: DownloadPipelineArtifact@0
+ displayName: "Download benchmarks"
+ inputs:
+ artifactName: benchmarks
+ targetPath: gh-pages/benchmarks
- task: DownloadPipelineArtifact@0
displayName: "Download dist - windows"
inputs:
diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml
new file mode 100644
index 00000000..7ffef776
--- /dev/null
+++ b/benchmarks/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "wasm-bindgen-benchmark"
+version = "0.1.0"
+authors = ["The wasm-bindgen Developers"]
+
+[dependencies]
+wasm-bindgen = "0.2.43"
+web-sys = { version = "0.3.20", features = ['Node'] }
+
+[lib]
+crate-type = ['cdylib']
diff --git a/benchmarks/README.md b/benchmarks/README.md
new file mode 100644
index 00000000..381c7913
--- /dev/null
+++ b/benchmarks/README.md
@@ -0,0 +1,54 @@
+# Microbenchmarks for `wasm-bindgen`
+
+This folder houses a number of microbenchmarks for `wasm-bindgen`. These, like
+all microbenchmarks, should be taken with a grain of salt. They are intended to
+help developers understand changes over time, but they are not intended to be a
+performance suite for WebAssembly for Rust.
+
+[View benchmarks for `master` branch online][online]
+
+[online]: https://rustwasm.github.io/wasm-bindgen/benchmarks/
+
+## Building and Running
+
+First, copy the benchmarks to a temporary directory:
+
+```
+$ cp ./benchmarks /some/other/directory
+```
+
+Next, `cd` into that directory and execute:
+
+```
+$ wasm-pack build --target web
+```
+
+Next, use your favorite static file server to host the current directory. For
+example using the [`https` crate](https://crates.io/crates/https):
+
+```
+$ http
+```
+
+Then open up a web browser and view http://localhost:8000, for example.
+
+You should be presented a page with lots of `(run)` links, where when you click
+them it will execute the benchmark and then display the result.
+
+## Benchmark Architecture
+
+Currently benchmarks are pretty bare bones. They just use benchmark.js to
+generate statistics which are then rendered to the screen. Benchmarks are listed
+one-by-one in `index.html` where a `td` exists for each benchmark. In `index.js`
+each of the `td`'s `id` properties are hooked up to an actual function to
+benchmark, depending on what's being benchmarked.
+
+Relevant files are:
+
+* `index.html` - the page showing all benchmarks
+* `index.js` - the driver JS for all benchmarks
+* `globals.js` - global JS functions imported by all other benchmarks
+* `js-bencharks.js` - the JS functions that we're benchmarking
+* `src/lib.rs` - the Rust/`wasm-bindgen` functions we're benchmarking
+* `raw.wast`/`raw.wasm` - a raw handwritten WebAssembly file used in some
+ benchmarks. A compiled version of this is checked into the repository.
diff --git a/benchmarks/globals.js b/benchmarks/globals.js
new file mode 100644
index 00000000..0412158e
--- /dev/null
+++ b/benchmarks/globals.js
@@ -0,0 +1,5 @@
+export function jsthunk() {}
+export function add(a, b) { return a + b; }
+export class Foo {
+ bar() {}
+}
diff --git a/benchmarks/index.html b/benchmarks/index.html
new file mode 100644
index 00000000..83a99337
--- /dev/null
+++ b/benchmarks/index.html
@@ -0,0 +1,295 @@
+
+
+
+
+
+
+
+ Source code
+
+ JS / wasm-bindgen comparison
+
+
+ These benchmarks are meant to compare WebAssembly costs using raw wasm
+ files and wasm-bindgen itself against the JS equivalents. These
+ microbenchmarks aren't really representative of WebAssembly performance,
+ but can be useful data points about how expensive it is to cross
+ boundaries for example.
+
+
+ For all benchmarks higher numbers are better numbers.
+
+
+
+
+
+ Benchmark |
+ wasm-bindgen |
+ JS |
+ *.wast |
+
+
+
+
+
+ Call a thunk
+
+ (?)
+
+
+ This benchmarks tests out how long it take JS to call a thunk in
+ the given language. For example JS will call a JS thunk or
+ JS will call a wasm function that does nothing.
+
+ |
+
+ |
+ |
+ |
+
+
+
+ Call an adder
+
+ (?)
+
+
+ This benchmarks tests out how long it take JS to call a function
+ in the target language which adds two numbers and returns the
+ result. This is likely to be similar to the previous benchmark
+ in terms of results, but it's thought that some extra computation
+ may show overheads differently.
+
+ |
+
+ |
+ |
+ |
+
+
+
+ Call a JS thunk
+
+ (?)
+
+
+ This benchmarks tests out how long it takes to call a JS function
+ 10,000 times. Each language/framework has a function which takes a
+ parameter of how many times to call an imported JS function.
+ Remember that JS itself benefits from inlining, where as JS cannot
+ be inlined into WebAssembly. In these cases the imported function
+ performs no work, it just returns immediately.
+
+ |
+
+ |
+ |
+ |
+
+
+
+
+ Call a JS function which adds
+
+ (?)
+
+
+ This benchmark is similar to the previous thunk benchmark except
+ that this time the imported function will add its two arguments
+ and returns the result. This helps measure the overhead of
+ sending data like integers back and forth.
+
+ |
+
+ |
+ |
+ |
+
+
+
+
+ Calculate Fib(40)
+
+ (?)
+
+
+ This benchmarks calculates the 40th fibonacci number. It in
+ theory should favor wasm since wasm is "better a compute", but
+ a good JIT will probably make the code roughly equivalent.
+
+ |
+
+ |
+ |
+
+
+
+
+ Access Node.firstChild
+
+ (?)
+
+
+ This benchmark attempts to see the cost of accessing a DOM
+ property from both JS and WebAssembly. We access the DOM property
+ as fast as possible in WebAssembly and otherwise just access it
+ as a normal property in JS.
+
+ |
+
+ |
+ |
+
+
+
+
+ Access Node.nodeType
+
+ (?)
+
+
+ This benchmark attempts to see the cost of accessing a DOM
+ property from both JS and WebAssembly. We access the DOM property
+ as fast as possible in WebAssembly and otherwise just access it
+ as a normal property in JS.
+
+ |
+
+ |
+ |
+
+
+
+
+ Count types of nodes on a page
+
+ (?)
+
+
+ This is intended to be a "flavorful DOM benchmark" which
+ exercises DOM functionality from WebAssembly, specifically
+ counting the number of types of each node on a page.
+
+ |
+
+ |
+ |
+
+
+
+
+ wasm-bindgen benchmarks
+
+
+ These benchmarks don't compare against JS but are instead more intended
+ to be compared against each other, across browsers, or across versions of
+ wasm-bindgen.
+
+
+
+
+
+ Benchmark |
+ Result |
+
+
+
+
+
+ Access Node.nodeType with final
+
+ (?)
+
+
+ This is similar to the Node.nodeType benchmark above
+ except that it uses the final attribute in
+ wasm-bindgen.
+
+ |
+
+ |
+
+
+
+ Access Node.nodeType with structural
+
+ (?)
+
+
+ This is similar to the Node.nodeType benchmark above
+ except that it uses the structural attribute in
+ wasm-bindgen.
+
+ |
+
+ |
+
+
+
+
+ Call a custom JS class Foo.bar method with
+ final
+
+ (?)
+
+
+ This is similar to the Node.nodeType benchmark above
+ except that it's not calling a DOM method but rather a custom JS
+ class's method.
+
+ |
+
+ |
+
+
+
+ Call a custom JS class Foo.bar method with
+ structural
+
+ (?)
+
+
+ This is similar to the Node.nodeType benchmark above
+ except that it's not calling a DOM method but rather a custom JS
+ class's method.
+
+ |
+
+ |
+
+
+
+
+ Pass to/from wasm-bindgen
+
+ (?)
+
+
+ This benchmarks the overhead of passing strings to wasm and
+ also receiving them from wasm.
+
+ |
+
+ |
+
+
+
+
+
diff --git a/benchmarks/index.js b/benchmarks/index.js
new file mode 100644
index 00000000..ac603b63
--- /dev/null
+++ b/benchmarks/index.js
@@ -0,0 +1,182 @@
+// Benchmarking framework that we're using
+import 'https://unpkg.com/lodash@4.17.11/lodash.js';
+import 'https://unpkg.com/benchmark@2.1.4/benchmark.js';
+
+// Import lots of functions from JS/wasm, and rename everything to have the
+// namespace of where it's coming from.
+import wbindgen_init, {
+ call_js_thunk_n_times as wbindgen_call_js_thunk_n_times,
+ call_js_add_n_times as wbindgen_call_js_add_n_times,
+ thunk as wbindgen_thunk,
+ add as wbindgen_add,
+ fibonacci as wbindgen_fibonacci,
+ call_node_first_child_n_times as wbindgen_call_node_first_child_n_times,
+ call_node_node_type_n_times as wbindgen_call_node_node_type_n_times,
+ count_node_types as wbindgen_count_node_types,
+ call_first_child_final_n_times as wbindgen_call_first_child_final_n_times,
+ call_first_child_structural_n_times as wbindgen_call_first_child_structural_n_times,
+ call_foo_bar_final_n_times as wbindgen_call_foo_bar_final_n_times,
+ call_foo_bar_structural_n_times as wbindgen_call_foo_bar_structural_n_times,
+ str_roundtrip as wbindgen_str_roundtrip,
+} from './pkg/wasm_bindgen_benchmark.js';
+import {
+ call_js_thunk_n_times as js_call_js_thunk_n_times,
+ call_js_add_n_times as js_call_js_add_n_times,
+ thunk as js_thunk,
+ add as js_add,
+ fibonacci as js_fibonacci,
+ call_node_first_child_n_times as js_call_node_first_child_n_times,
+ call_node_node_type_n_times as js_call_node_node_type_n_times,
+ count_node_types as js_count_node_types,
+} from './js-benchmarks.js';
+import * as globals from './globals.js';
+import { Lock } from './utils.js';
+
+// These are set for `raw.wasm`, which we import and configure manually:
+let raw_call_js_thunk_n_times = null;
+let raw_call_js_add_n_times = null;
+let raw_thunk = null;
+let raw_add = null;
+
+// Create a `Map` of all benchmarks that we're going to execute, where the map
+// is from a benchmark's name to a thunk to execute for the benchmark.
+function makeBenchmarks() {
+ const benchmarks = new Map();
+
+ benchmarks.wbindgen_thunk = wbindgen_thunk;
+ benchmarks.raw_thunk = raw_thunk;
+ benchmarks.js_thunk = js_thunk;
+
+ benchmarks.wbindgen_fib_40 = () => wbindgen_fibonacci(40);
+ benchmarks.js_fib_40 = () => js_fibonacci(40);
+
+ benchmarks.wbindgen_add = () => wbindgen_add(2, 3);
+ benchmarks.raw_add = () => raw_add(2, 3);
+ benchmarks.js_add = () => js_add(2, 3);
+
+ benchmarks.js_call_js_thunk_n_times = () => js_call_js_thunk_n_times(10000);
+ benchmarks.raw_call_js_thunk_n_times = () => raw_call_js_thunk_n_times(10000);
+ benchmarks.wbindgen_call_js_thunk_n_times = () => wbindgen_call_js_thunk_n_times(10000);
+
+ benchmarks.js_call_js_add_n_times = () => js_call_js_add_n_times(10000, 2, 3);
+ benchmarks.raw_call_js_add_n_times = () => raw_call_js_add_n_times(10000, 2, 3);
+ benchmarks.wbindgen_call_js_add_n_times = () => wbindgen_call_js_add_n_times(10000, 2, 3);
+
+ const list = [];
+ for (let i = 0; i < 10; i++)
+ list.push(document.body);
+ benchmarks.wbindgen_call_node_first_child_n_times = () => wbindgen_call_node_first_child_n_times(1000, list);
+ benchmarks.js_call_node_first_child_n_times = () => js_call_node_first_child_n_times(1000, list);
+ benchmarks.wbindgen_call_node_node_type_n_times = () => wbindgen_call_node_node_type_n_times(1000, list);
+ benchmarks.js_call_node_node_type_n_times = () => js_call_node_node_type_n_times(1000, list);
+
+ const body = document.body;
+ benchmarks.wbindgen_count_node_types = () => wbindgen_count_node_types(body);
+ benchmarks.js_count_node_types = () => js_count_node_types(body);
+
+ benchmarks.wbindgen_call_first_child_final_n_times = () => wbindgen_call_first_child_final_n_times(10000, body);
+ benchmarks.wbindgen_call_first_child_structural_n_times = () => wbindgen_call_first_child_structural_n_times(10000, body);
+
+ const foo = new globals.Foo();
+ benchmarks.wbindgen_call_foo_bar_final_n_times = () => wbindgen_call_foo_bar_final_n_times(10000, foo);
+ benchmarks.wbindgen_call_foo_bar_structural_n_times = () => wbindgen_call_foo_bar_structural_n_times(10000, foo);
+
+
+ const strings = {
+ ascii_small: 'ja',
+ ascii_medium: 'aym0566x',
+ ascii_number: '505874924095815681',
+ ascii_date: 'Sun Aug 31 00:29:15 +0000 2014',
+ ascii_url: 'https://pbs.twimg.com/profile_images/497760886795153410/LDjAwR_y_normal.jpeg',
+ ascii_link: 'Twitter for iPhone',
+ unicode: '@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる?:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖'
+ }
+ const template = document.querySelector('tr.str-benchmark');
+ template.remove();
+ const tbody = document.querySelector('tbody#wbindgen-body');
+ for (const bm in strings) {
+ const s = strings[bm];
+ const bm_name = `wbindgen_str_${bm}`;
+ benchmarks[bm_name] = () => wbindgen_str_roundtrip(s);
+
+ const row = template.cloneNode(true);
+ row.querySelector('.str').textContent = bm;
+ row.querySelector('td.bm').id = bm_name;
+ row.removeAttribute('style');
+ tbody.appendChild(row);
+ }
+
+ return benchmarks;
+}
+
+// Set up the page and initialize all event handlers and such.
+function run() {
+ const benchmarks = makeBenchmarks();
+
+ const benchmarkLock = new Lock();
+ for (const td of document.querySelectorAll('td.bm')) {
+ const bm = benchmarks[td.id];
+ if (typeof bm !== 'function')
+ throw new Error(`no benchmark registered for ${td.id}`);
+
+ const run = document.createElement('a');
+ run.href = '#';
+ run.innerText = '(run)';
+ run.onclick = function() {
+ benchmarkLock.withLock(async () => {
+ await executeAndUpdate(td.id, bm, td);
+ });
+ run.remove();
+ td.innerText = 'executing ...';
+ return false;
+ };
+ td.appendChild(run);
+ }
+
+ for (const a of document.querySelectorAll('.about-open')) {
+ a.onclick = function() {
+ a.nextElementSibling.style.display = 'block';
+ a.remove();
+ return false;
+ };
+ }
+}
+
+async function executeAndUpdate(name, bm, td) {
+ const result = await executeBenchmark(name, bm);
+ console.log(result.target);
+ const rme = Math.round(result.target.stats.rme * 100) / 100;
+ td.innerText = `${Math.round(result.target.hz).toLocaleString()}/s ±${rme}%`;
+}
+
+function executeBenchmark(name, bm) {
+ return new Promise((resolve, reject) => {
+ const suite = new Benchmark.Suite();
+ suite.add(name, bm);
+ suite.on('cycle', resolve);
+ suite.run({ async: true });
+ });
+}
+
+// Load wasm files and when they're done (plus the DOM) then we initialize
+// everything
+const wasms = [];
+wasms.push(wbindgen_init('./pkg/wasm_bindgen_benchmark_bg.wasm'));
+wasms.push(fetch('./raw.wasm')
+ .then(r => r.arrayBuffer())
+ .then(m => WebAssembly.instantiate(m, { './globals.js': globals }))
+ .then(m => {
+ raw_call_js_thunk_n_times = m.instance.exports.call_js_thunk_n_times;
+ raw_call_js_add_n_times = m.instance.exports.call_js_add_n_times;
+ raw_thunk = m.instance.exports.thunk;
+ raw_add = m.instance.exports.add;
+ }));
+
+Promise.all(wasms)
+ .then(() => {
+ if (document.readyState === 'loading')
+ document.addEventListener('DOMContentLoaded', run);
+ else
+ run();
+ })
+ .catch(console.error);
diff --git a/benchmarks/js-benchmarks.js b/benchmarks/js-benchmarks.js
new file mode 100644
index 00000000..d6b56614
--- /dev/null
+++ b/benchmarks/js-benchmarks.js
@@ -0,0 +1,76 @@
+import { jsthunk, add as jsadd } from './globals.js';
+
+export function fibonacci(n) {
+ let a = 1;
+ let b = 1;
+ let tmp = 0;
+
+ while (n > 1) {
+ tmp = b;
+ b += a;
+ a = tmp;
+ --n;
+ }
+
+ return a;
+}
+
+export function thunk() {}
+
+export function call_js_thunk_n_times(n) {
+ for (var i = 0; i < n; i++) {
+ jsthunk();
+ }
+}
+
+export function add(a, b) {
+ return a + b;
+}
+
+export function call_js_add_n_times(n, a, b) {
+ for (var i = 0; i < n; i++) {
+ jsadd(a, b);
+ }
+}
+
+export function call_node_first_child_n_times(n, array_of_elements) {
+ for (let i = 0; i < n; i++) {
+ for (const element of array_of_elements)
+ if (element.firstChild === null)
+ throw new Error("bad");
+ }
+}
+
+export function call_node_node_type_n_times(n, array_of_elements) {
+ for (let i = 0; i < n; i++) {
+ for (const element of array_of_elements)
+ if (element.nodeType === 100)
+ throw new Error("bad");
+ }
+}
+
+export function call_node_has_child_nodes_n_times(n, array_of_elements) {
+ for (let i = 0; i < n; i++) {
+ for (const element of array_of_elements)
+ if (!element.hasChildNodes())
+ throw new Error("bad");
+ }
+}
+
+export function count_node_types(element) {
+ const types = [];
+
+ function count(node, types) {
+ while(node) {
+ const type = node.nodeType;
+ while (types.length <= type)
+ types.push(0);
+ types[type] += 1;
+ count(node.firstChild, types);
+ node = node.nextSibling;
+ }
+ }
+
+ count(element, types);
+ return types;
+}
diff --git a/benchmarks/raw.wasm b/benchmarks/raw.wasm
new file mode 100644
index 00000000..00580e29
Binary files /dev/null and b/benchmarks/raw.wasm differ
diff --git a/benchmarks/raw.wast b/benchmarks/raw.wast
new file mode 100644
index 00000000..97298226
--- /dev/null
+++ b/benchmarks/raw.wast
@@ -0,0 +1,52 @@
+(module
+ (import "./globals.js" "jsthunk" (func $js_thunk))
+ (import "./globals.js" "add" (func $js_add (param i32) (param i32) (result i32)))
+
+ (export "call_js_thunk_n_times" (func $call_thunk))
+ (export "call_js_add_n_times" (func $call_add))
+ (export "thunk" (func $thunk))
+ (export "add" (func $add))
+
+ (func $call_thunk (param i32)
+ block
+ get_local 0
+ i32.eqz
+ br_if 0
+ loop
+ call $js_thunk
+ get_local 0
+ i32.const 1
+ i32.sub
+ tee_local 0
+ br_if 0
+ end
+ end
+ )
+
+ (func $call_add (param i32) (param i32) (param i32)
+ block
+ get_local 0
+ i32.eqz
+ br_if 0
+ loop
+ get_local 2
+ get_local 1
+ call $js_add
+ drop
+ get_local 0
+ i32.const 1
+ i32.sub
+ tee_local 0
+ br_if 0
+ end
+ end
+ )
+
+ (func $thunk)
+
+ (func $add (param i32) (param i32) (result i32)
+ get_local 0
+ get_local 1
+ i32.add
+ )
+)
diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs
new file mode 100644
index 00000000..2b8e1736
--- /dev/null
+++ b/benchmarks/src/lib.rs
@@ -0,0 +1,179 @@
+extern crate wasm_bindgen;
+extern crate web_sys;
+
+use wasm_bindgen::prelude::*;
+use wasm_bindgen::JsCast;
+use web_sys::Node;
+
+#[wasm_bindgen(raw_module = "../globals.js")]
+extern {
+ #[wasm_bindgen(js_name = jsthunk)]
+ fn js_thunk();
+ #[wasm_bindgen(js_name = add)]
+ fn js_add(a: i32, b: i32) -> i32;
+
+ pub type Foo;
+ #[wasm_bindgen(method, final, js_name = bar)]
+ fn bar_final(this: &Foo);
+ #[wasm_bindgen(method, structural, js_name = bar)]
+ fn bar_structural(this: &Foo);
+
+ #[wasm_bindgen(js_name = jsthunk)]
+ fn doesnt_throw();
+ #[wasm_bindgen(catch, js_name = jsthunk)]
+ fn doesnt_throw_catch() -> Result<(), JsValue>;
+}
+
+#[wasm_bindgen]
+pub fn call_js_thunk_n_times(n: usize) {
+ for _ in 0..n {
+ js_thunk();
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_js_add_n_times(n: usize, a: i32, b: i32) {
+ for _ in 0..n {
+ js_add(a, b);
+ }
+}
+
+#[wasm_bindgen]
+pub fn thunk() {}
+
+#[wasm_bindgen]
+pub fn add(a: i32, b: i32) -> i32 {
+ a + b
+}
+
+static mut FIB_HIGH: i32 = 0;
+
+#[wasm_bindgen]
+pub fn fibonacci(n: i32) -> i32 {
+ let mut a = 1u64;
+ let mut b = 1;
+ for _ in 0..n {
+ let tmp = b;
+ b += a;
+ a = tmp;
+ }
+ unsafe { FIB_HIGH = (a >> 32) as i32; }
+ return a as i32
+}
+
+#[wasm_bindgen]
+pub fn fibonacci_high() -> i32 {
+ unsafe { FIB_HIGH }
+}
+
+#[wasm_bindgen]
+pub fn call_foo_bar_final_n_times(n: usize, foo: &Foo) {
+ for _ in 0..n {
+ foo.bar_final();
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_foo_bar_structural_n_times(n: usize, foo: &Foo) {
+ for _ in 0..n {
+ foo.bar_structural();
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_doesnt_throw_n_times(n: usize) {
+ for _ in 0..n {
+ doesnt_throw();
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_doesnt_throw_with_catch_n_times(n: usize) {
+ for _ in 0..n {
+ if let Err(e) = doesnt_throw_catch() {
+ wasm_bindgen::throw_val(e);
+ }
+ }
+}
+
+#[wasm_bindgen]
+extern {
+ pub type Element;
+
+ #[wasm_bindgen(method, js_name = firstChild, final, getter)]
+ fn first_child_final(this: &Element) -> Element;
+ #[wasm_bindgen(method, js_name = firstChild, structural, getter)]
+ fn first_child_structural(this: &Element) -> Element;
+}
+
+#[wasm_bindgen]
+pub fn call_first_child_final_n_times(n: usize, element: &Element) {
+ for _ in 0..n {
+ drop(element.first_child_final());
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_first_child_structural_n_times(n: usize, element: &Element) {
+ for _ in 0..n {
+ drop(element.first_child_structural());
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_node_first_child_n_times(n: usize, elements: Vec) {
+ for _ in 0..n {
+ for element in elements.iter() {
+ let element = element.unchecked_ref::();
+ assert!(element.first_child().is_some());
+ }
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_node_node_type_n_times(n: usize, elements: Vec) {
+ for _ in 0..n {
+ for element in elements.iter() {
+ let element = element.unchecked_ref::();
+ assert!(element.node_type() != 100);
+ }
+ }
+}
+
+#[wasm_bindgen]
+pub fn call_node_has_child_nodes_n_times(n: usize, elements: Vec) {
+ for _ in 0..n {
+ for element in elements.iter() {
+ let element = element.unchecked_ref::();
+ assert!(element.has_child_nodes());
+ }
+ }
+}
+
+#[wasm_bindgen]
+pub fn count_node_types(element: Node) {
+ let mut count = Vec::new();
+ count_node_types(element, &mut count);
+
+ fn count_node_types(mut element: Node, count: &mut Vec) {
+ loop {
+ let t = element.node_type();
+ if t as usize >= count.len() {
+ count.resize(t as usize + 1, 0);
+ }
+ count[t as usize] += 1;
+ if let Some(s) = element.first_child() {
+ count_node_types(s, count);
+ }
+ match element.next_sibling() {
+ Some(s) => element = s,
+ None => break,
+ }
+ }
+ }
+}
+
+#[wasm_bindgen]
+pub fn str_roundtrip(s: String) -> String {
+ s
+}
diff --git a/benchmarks/utils.js b/benchmarks/utils.js
new file mode 100644
index 00000000..d2b4af24
--- /dev/null
+++ b/benchmarks/utils.js
@@ -0,0 +1,14 @@
+export class Lock {
+ constructor() {
+ this.lockHolder = null;
+ }
+
+ async withLock(scope) {
+ while (this.lockHolder !== null) {
+ await this.lockHolder;
+ }
+ this.lockHolder = Promise.resolve(null).then(scope);
+ await this.lockHolder;
+ this.lockHolder = null;
+ }
+}
diff --git a/ci/azure-install-wasm-pack.yml b/ci/azure-install-wasm-pack.yml
new file mode 100644
index 00000000..4759d82e
--- /dev/null
+++ b/ci/azure-install-wasm-pack.yml
@@ -0,0 +1,8 @@
+steps:
+ - script: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
+ displayName: "install wasm-pack"
+ - script: |
+ set -ex
+ cargo build -p wasm-bindgen-cli
+ ln -snf `pwd`/target/debug/wasm-bindgen $HOME/.cargo/bin/wasm-bindgen
+ displayName: "install wasm-bindgen for `wasm-pack` to use"