mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-15 12:44:31 +03:00
Merge pull request #1764 from fitzgen/multi-value-xform
Use multi-value with interface types
This commit is contained in:
commit
04c9b32e34
@ -14,7 +14,8 @@ jobs:
|
|||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
- template: ci/azure-install-geckodriver.yml
|
- template: ci/azure-install-geckodriver.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo test
|
- script: cargo test
|
||||||
displayName: "Builds on native"
|
displayName: "Builds on native"
|
||||||
- script: cargo test --target wasm32-unknown-unknown
|
- script: cargo test --target wasm32-unknown-unknown
|
||||||
@ -31,6 +32,8 @@ jobs:
|
|||||||
displayName: "Futures test suite on native"
|
displayName: "Futures test suite on native"
|
||||||
- script: cargo test -p wasm-bindgen-futures --target wasm32-unknown-unknown
|
- script: cargo test -p wasm-bindgen-futures --target wasm32-unknown-unknown
|
||||||
displayName: "Futures test suite on wasm"
|
displayName: "Futures test suite on wasm"
|
||||||
|
- script: cargo test -p wasm-bindgen-multi-value-xform
|
||||||
|
displayName: "multi-value xform tests on native"
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
curl https://nodejs.org/download/nightly/v13.0.0-nightly2019081215b2d13310/node-v13.0.0-nightly2019081215b2d13310-linux-x64.tar.xz | tar xJf -
|
curl https://nodejs.org/download/nightly/v13.0.0-nightly2019081215b2d13310/node-v13.0.0-nightly2019081215b2d13310-linux-x64.tar.xz | tar xJf -
|
||||||
@ -56,7 +59,8 @@ jobs:
|
|||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
- template: ci/azure-install-geckodriver.yml
|
- template: ci/azure-install-geckodriver.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo test --target wasm32-unknown-unknown
|
- script: cargo test --target wasm32-unknown-unknown
|
||||||
displayName: "wasm-bindgen test suite"
|
displayName: "wasm-bindgen test suite"
|
||||||
env:
|
env:
|
||||||
@ -78,14 +82,16 @@ jobs:
|
|||||||
parameters:
|
parameters:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo test --target wasm32-unknown-unknown --features nightly --test wasm
|
- script: cargo test --target wasm32-unknown-unknown --features nightly --test wasm
|
||||||
|
|
||||||
- job: test_cli
|
- job: test_cli
|
||||||
displayName: "Run wasm-bindgen-cli crate tests"
|
displayName: "Run wasm-bindgen-cli crate tests"
|
||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: rustup target add wasm32-unknown-unknown
|
- script: rustup target add wasm32-unknown-unknown
|
||||||
displayName: "install wasm target"
|
displayName: "install wasm target"
|
||||||
- script: cargo test -p wasm-bindgen-cli-support
|
- script: cargo test -p wasm-bindgen-cli-support
|
||||||
@ -102,7 +108,8 @@ jobs:
|
|||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
- template: ci/azure-install-geckodriver.yml
|
- template: ci/azure-install-geckodriver.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown
|
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown
|
||||||
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Node
|
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Node
|
||||||
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Element
|
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Element
|
||||||
@ -118,7 +125,8 @@ jobs:
|
|||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
- template: ci/azure-install-geckodriver.yml
|
- template: ci/azure-install-geckodriver.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo test -p js-sys --target wasm32-unknown-unknown
|
- script: cargo test -p js-sys --target wasm32-unknown-unknown
|
||||||
|
|
||||||
- job: test_webidl
|
- job: test_webidl
|
||||||
@ -129,7 +137,8 @@ jobs:
|
|||||||
parameters:
|
parameters:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
#- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo test -p wasm-bindgen-webidl
|
- script: cargo test -p wasm-bindgen-webidl
|
||||||
- script: cargo test -p webidl-tests --target wasm32-unknown-unknown
|
- script: cargo test -p webidl-tests --target wasm32-unknown-unknown
|
||||||
env:
|
env:
|
||||||
@ -143,19 +152,23 @@ jobs:
|
|||||||
parameters:
|
parameters:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo test -p wasm-bindgen-macro
|
- script: cargo test -p wasm-bindgen-macro
|
||||||
|
|
||||||
- job: test_wasm_interpreter
|
- job: test_wasm_interpreter
|
||||||
displayName: "Run wasm-bindgen-wasm-interpreter tests"
|
displayName: "Run wasm-bindgen-wasm-interpreter tests"
|
||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: |
|
- script: |
|
||||||
git clone https://github.com/WebAssembly/wabt
|
git clone https://github.com/WebAssembly/wabt
|
||||||
mkdir -p wabt/build
|
mkdir -p wabt/build
|
||||||
cd wabt/build
|
cd wabt/build
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=off -DCMAKE_CXX_COMPILER_LAUNCHER=$RUSTC_WRAPPER
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=off -DCMAKE_CXX_COMPILER_LAUNCHER=$RUSTC_WRAPPER
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=off
|
||||||
cmake --build . -- -j$(nproc)
|
cmake --build . -- -j$(nproc)
|
||||||
echo "##vso[task.prependpath]$PWD"
|
echo "##vso[task.prependpath]$PWD"
|
||||||
- script: cargo test -p wasm-bindgen-wasm-interpreter
|
- script: cargo test -p wasm-bindgen-wasm-interpreter
|
||||||
@ -164,7 +177,8 @@ jobs:
|
|||||||
displayName: "Test TypeScript output of wasm-bindgen"
|
displayName: "Test TypeScript output of wasm-bindgen"
|
||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- template: ci/azure-install-node.yml
|
- template: ci/azure-install-node.yml
|
||||||
- script: cd crates/typescript-tests && ./run.sh
|
- script: cd crates/typescript-tests && ./run.sh
|
||||||
|
|
||||||
@ -175,7 +189,8 @@ jobs:
|
|||||||
# TODO: switch this back to `stable` when async/await is stable
|
# TODO: switch this back to `stable` when async/await is stable
|
||||||
parameters:
|
parameters:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- template: ci/azure-install-wasm-pack.yml
|
- template: ci/azure-install-wasm-pack.yml
|
||||||
- script: mv _package.json package.json && npm install && rm package.json
|
- script: mv _package.json package.json && npm install && rm package.json
|
||||||
displayName: "run npm install"
|
displayName: "run npm install"
|
||||||
@ -197,7 +212,8 @@ jobs:
|
|||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
parameters:
|
parameters:
|
||||||
toolchain: nightly-2019-08-27
|
toolchain: nightly-2019-08-27
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: rustup component add rust-src
|
- script: rustup component add rust-src
|
||||||
displayName: "install rust-src"
|
displayName: "install rust-src"
|
||||||
- script: cargo install xargo
|
- script: cargo install xargo
|
||||||
@ -220,7 +236,8 @@ jobs:
|
|||||||
# TODO: switch this back to `stable` when async/await is stable
|
# TODO: switch this back to `stable` when async/await is stable
|
||||||
parameters:
|
parameters:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- template: ci/azure-install-wasm-pack.yml
|
- template: ci/azure-install-wasm-pack.yml
|
||||||
- script: wasm-pack build --target web benchmarks
|
- script: wasm-pack build --target web benchmarks
|
||||||
displayName: "build benchmarks"
|
displayName: "build benchmarks"
|
||||||
@ -235,7 +252,8 @@ jobs:
|
|||||||
displayName: "Dist Linux binary"
|
displayName: "Dist Linux binary"
|
||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: rustup target add x86_64-unknown-linux-musl
|
- script: rustup target add x86_64-unknown-linux-musl
|
||||||
- script: |
|
- script: |
|
||||||
sudo apt update -y
|
sudo apt update -y
|
||||||
@ -258,7 +276,8 @@ jobs:
|
|||||||
vmImage: macOS-10.13
|
vmImage: macOS-10.13
|
||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo build --manifest-path crates/cli/Cargo.toml --release
|
- script: cargo build --manifest-path crates/cli/Cargo.toml --release
|
||||||
env:
|
env:
|
||||||
MACOSX_DEPLOYMENT_TARGET: 10.7
|
MACOSX_DEPLOYMENT_TARGET: 10.7
|
||||||
@ -272,15 +291,17 @@ jobs:
|
|||||||
vmImage: vs2017-win2016
|
vmImage: vs2017-win2016
|
||||||
steps:
|
steps:
|
||||||
- template: ci/azure-install-rust.yml
|
- template: ci/azure-install-rust.yml
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo build --manifest-path crates/cli/Cargo.toml --release
|
- script: cargo build --manifest-path crates/cli/Cargo.toml --release
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: -Ctarget-feature=+crt-static
|
RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||||
- template: ci/azure-create-tarball.yml
|
- template: ci/azure-create-tarball.yml
|
||||||
parameters:
|
parameters:
|
||||||
name: dist_windows
|
name: dist_windows
|
||||||
- script: "%RUSTC_WRAPPER% -s"
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
- script: cat sccache.log
|
# - script: "%RUSTC_WRAPPER% -s"
|
||||||
|
# - script: cat sccache.log
|
||||||
|
|
||||||
- job: doc_book
|
- job: doc_book
|
||||||
displayName: "Doc - build the book"
|
displayName: "Doc - build the book"
|
||||||
@ -306,7 +327,8 @@ jobs:
|
|||||||
# Install rustfmt so we can format the web-sys bindings
|
# Install rustfmt so we can format the web-sys bindings
|
||||||
- script: rustup component add rustfmt
|
- script: rustup component add rustfmt
|
||||||
displayName: "Install rustfmt"
|
displayName: "Install rustfmt"
|
||||||
- template: ci/azure-install-sccache.yml
|
# Temporarily disable sccache because it is failing on CI.
|
||||||
|
# - template: ci/azure-install-sccache.yml
|
||||||
- script: cargo doc --no-deps --features 'nightly serde-serialize'
|
- script: cargo doc --no-deps --features 'nightly serde-serialize'
|
||||||
displayName: "Document wasm-bindgen"
|
displayName: "Document wasm-bindgen"
|
||||||
- script: cargo doc --no-deps --manifest-path crates/js-sys/Cargo.toml
|
- script: cargo doc --no-deps --manifest-path crates/js-sys/Cargo.toml
|
||||||
|
@ -13,4 +13,4 @@ edition = '2018'
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
walrus = "0.11.0"
|
walrus = "0.12.0"
|
||||||
|
@ -18,9 +18,11 @@ log = "0.4"
|
|||||||
rustc-demangle = "0.1.13"
|
rustc-demangle = "0.1.13"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tempfile = "3.0"
|
tempfile = "3.0"
|
||||||
walrus = "0.11.0"
|
walrus = "0.12.0"
|
||||||
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.50' }
|
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.50' }
|
||||||
wasm-bindgen-shared = { path = "../shared", version = '=0.2.50' }
|
wasm-bindgen-shared = { path = "../shared", version = '=0.2.50' }
|
||||||
|
wasm-bindgen-multi-value-xform = { path = '../multi-value-xform', version = '=0.2.50' }
|
||||||
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.50' }
|
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.50' }
|
||||||
|
wasm-bindgen-wasm-conventions = { path = '../wasm-conventions', version = '=0.2.50' }
|
||||||
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.50' }
|
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.50' }
|
||||||
wasm-webidl-bindings = "0.4.0"
|
wasm-webidl-bindings = "0.5.0"
|
||||||
|
@ -465,12 +465,10 @@ impl<'a> Context<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let default_module_path = match self.config.mode {
|
let default_module_path = match self.config.mode {
|
||||||
OutputMode::Web => {
|
OutputMode::Web => "\
|
||||||
"\
|
|
||||||
if (typeof module === 'undefined') {
|
if (typeof module === 'undefined') {
|
||||||
module = import.meta.url.replace(/\\.js$/, '_bg.wasm');
|
module = import.meta.url.replace(/\\.js$/, '_bg.wasm');
|
||||||
}"
|
}",
|
||||||
}
|
|
||||||
_ => "",
|
_ => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -872,21 +870,30 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
match self.config.encode_into {
|
match self.config.encode_into {
|
||||||
EncodeInto::Always if !shared => {
|
EncodeInto::Always if !shared => {
|
||||||
self.global(&format!("
|
self.global(&format!(
|
||||||
|
"
|
||||||
const encodeString = {};
|
const encodeString = {};
|
||||||
", encode_into));
|
",
|
||||||
|
encode_into
|
||||||
|
));
|
||||||
}
|
}
|
||||||
EncodeInto::Test if !shared => {
|
EncodeInto::Test if !shared => {
|
||||||
self.global(&format!("
|
self.global(&format!(
|
||||||
|
"
|
||||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||||
? {}
|
? {}
|
||||||
: {});
|
: {});
|
||||||
", encode_into, encode));
|
",
|
||||||
|
encode_into, encode
|
||||||
|
));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.global(&format!("
|
self.global(&format!(
|
||||||
|
"
|
||||||
const encodeString = {};
|
const encodeString = {};
|
||||||
", encode));
|
",
|
||||||
|
encode
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1080,7 +1087,6 @@ impl<'a> Context<'a> {
|
|||||||
fields: Vec::new(),
|
fields: Vec::new(),
|
||||||
})?;
|
})?;
|
||||||
self.global(&format!("let cached{} = new {}{};", s, name, args));
|
self.global(&format!("let cached{} = new {}{};", s, name, args));
|
||||||
|
|
||||||
} else if !self.config.mode.always_run_in_browser() {
|
} else if !self.config.mode.always_run_in_browser() {
|
||||||
self.global(&format!(
|
self.global(&format!(
|
||||||
"
|
"
|
||||||
@ -1090,7 +1096,6 @@ impl<'a> Context<'a> {
|
|||||||
s
|
s
|
||||||
));
|
));
|
||||||
self.global(&format!("let cached{0} = new l{0}{1};", s, args));
|
self.global(&format!("let cached{0} = new l{0}{1};", s, args));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
self.global(&format!("let cached{0} = new {0}{1};", s, args));
|
self.global(&format!("let cached{0} = new {0}{1};", s, args));
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use std::mem;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str;
|
use std::str;
|
||||||
use walrus::Module;
|
use walrus::Module;
|
||||||
|
use wasm_bindgen_wasm_conventions as wasm_conventions;
|
||||||
|
|
||||||
mod anyref;
|
mod anyref;
|
||||||
mod decode;
|
mod decode;
|
||||||
@ -36,6 +37,7 @@ pub struct Bindgen {
|
|||||||
// "ready to be instantiated on any thread"
|
// "ready to be instantiated on any thread"
|
||||||
threads: wasm_bindgen_threads_xform::Config,
|
threads: wasm_bindgen_threads_xform::Config,
|
||||||
anyref: bool,
|
anyref: bool,
|
||||||
|
multi_value: bool,
|
||||||
wasm_interface_types: bool,
|
wasm_interface_types: bool,
|
||||||
encode_into: EncodeInto,
|
encode_into: EncodeInto,
|
||||||
}
|
}
|
||||||
@ -77,6 +79,7 @@ impl Bindgen {
|
|||||||
pub fn new() -> Bindgen {
|
pub fn new() -> Bindgen {
|
||||||
let anyref = env::var("WASM_BINDGEN_ANYREF").is_ok();
|
let anyref = env::var("WASM_BINDGEN_ANYREF").is_ok();
|
||||||
let wasm_interface_types = env::var("WASM_INTERFACE_TYPES").is_ok();
|
let wasm_interface_types = env::var("WASM_INTERFACE_TYPES").is_ok();
|
||||||
|
let multi_value = env::var("WASM_BINDGEN_MULTI_VALUE").is_ok();
|
||||||
Bindgen {
|
Bindgen {
|
||||||
input: Input::None,
|
input: Input::None,
|
||||||
out_name: None,
|
out_name: None,
|
||||||
@ -93,6 +96,7 @@ impl Bindgen {
|
|||||||
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
|
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
|
||||||
threads: threads_config(),
|
threads: threads_config(),
|
||||||
anyref: anyref || wasm_interface_types,
|
anyref: anyref || wasm_interface_types,
|
||||||
|
multi_value,
|
||||||
wasm_interface_types,
|
wasm_interface_types,
|
||||||
encode_into: EncodeInto::Test,
|
encode_into: EncodeInto::Test,
|
||||||
}
|
}
|
||||||
@ -275,6 +279,15 @@ impl Bindgen {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Our threads and multi-value xforms rely on the presence of the stack
|
||||||
|
// pointer, so temporarily export it so that our many GC's don't remove
|
||||||
|
// it before the xform runs.
|
||||||
|
let mut exported_shadow_stack_pointer = false;
|
||||||
|
if self.multi_value || self.threads.is_enabled() {
|
||||||
|
wasm_conventions::export_shadow_stack_pointer(&mut module)?;
|
||||||
|
exported_shadow_stack_pointer = true;
|
||||||
|
}
|
||||||
|
|
||||||
// This isn't the hardest thing in the world too support but we
|
// This isn't the hardest thing in the world too support but we
|
||||||
// basically don't know how to rationalize #[wasm_bindgen(start)] and
|
// basically don't know how to rationalize #[wasm_bindgen(start)] and
|
||||||
// the actual `start` function if present. Figure this out later if it
|
// the actual `start` function if present. Figure this out later if it
|
||||||
@ -320,7 +333,12 @@ impl Bindgen {
|
|||||||
// the webidl bindings proposal) as well as an auxiliary section for all
|
// the webidl bindings proposal) as well as an auxiliary section for all
|
||||||
// sorts of miscellaneous information and features #[wasm_bindgen]
|
// sorts of miscellaneous information and features #[wasm_bindgen]
|
||||||
// supports that aren't covered by WebIDL bindings.
|
// supports that aren't covered by WebIDL bindings.
|
||||||
webidl::process(&mut module, self.anyref, self.wasm_interface_types, self.emit_start)?;
|
webidl::process(
|
||||||
|
&mut module,
|
||||||
|
self.anyref,
|
||||||
|
self.wasm_interface_types,
|
||||||
|
self.emit_start,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Now that we've got type information from the webidl processing pass,
|
// Now that we've got type information from the webidl processing pass,
|
||||||
// touch up the output of rustc to insert anyref shims where necessary.
|
// touch up the output of rustc to insert anyref shims where necessary.
|
||||||
@ -335,7 +353,7 @@ impl Bindgen {
|
|||||||
.customs
|
.customs
|
||||||
.delete_typed::<webidl::WasmBindgenAux>()
|
.delete_typed::<webidl::WasmBindgenAux>()
|
||||||
.expect("aux section should be present");
|
.expect("aux section should be present");
|
||||||
let bindings = module
|
let mut bindings = module
|
||||||
.customs
|
.customs
|
||||||
.delete_typed::<webidl::NonstandardWebidlSection>()
|
.delete_typed::<webidl::NonstandardWebidlSection>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -350,8 +368,30 @@ impl Bindgen {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if self.wasm_interface_types {
|
if self.wasm_interface_types {
|
||||||
|
if self.multi_value {
|
||||||
|
webidl::standard::add_multi_value(&mut module, &mut bindings)
|
||||||
|
.context("failed to transform return pointers into multi-value Wasm")?;
|
||||||
|
}
|
||||||
webidl::standard::add_section(&mut module, &aux, &bindings)
|
webidl::standard::add_section(&mut module, &aux, &bindings)
|
||||||
.with_context(|_| "failed to generate a standard wasm bindings custom section")?;
|
.with_context(|_| "failed to generate a standard wasm bindings custom section")?;
|
||||||
|
} else {
|
||||||
|
if self.multi_value {
|
||||||
|
failure::bail!(
|
||||||
|
"Wasm multi-value is currently only available when \
|
||||||
|
Wasm interface types is also enabled"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we exported the shadow stack pointer earlier, remove it from the
|
||||||
|
// export set now.
|
||||||
|
if exported_shadow_stack_pointer {
|
||||||
|
wasm_conventions::unexport_shadow_stack_pointer(&mut module)?;
|
||||||
|
// The shadow stack pointer is potentially unused now, but since it
|
||||||
|
// most likely _is_ in use, we don't pay the cost of a full GC here
|
||||||
|
// just to remove one potentially unnecessary global.
|
||||||
|
//
|
||||||
|
// walrus::passes::gc::run(&mut module);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Output {
|
Ok(Output {
|
||||||
@ -535,16 +575,14 @@ impl Output {
|
|||||||
} else {
|
} else {
|
||||||
format!("{}_bg", self.stem)
|
format!("{}_bg", self.stem)
|
||||||
};
|
};
|
||||||
let wasm_path = out_dir
|
let wasm_path = out_dir.join(wasm_name).with_extension("wasm");
|
||||||
.join(wasm_name)
|
|
||||||
.with_extension("wasm");
|
|
||||||
fs::create_dir_all(out_dir)?;
|
fs::create_dir_all(out_dir)?;
|
||||||
let wasm_bytes = self.module.emit_wasm()?;
|
let wasm_bytes = self.module.emit_wasm();
|
||||||
fs::write(&wasm_path, wasm_bytes)
|
fs::write(&wasm_path, wasm_bytes)
|
||||||
.with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
|
.with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
|
||||||
|
|
||||||
if self.wasm_interface_types {
|
if self.wasm_interface_types {
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write out all local JS snippets to the final destination now that
|
// Write out all local JS snippets to the final destination now that
|
||||||
|
@ -165,7 +165,7 @@ impl Output {
|
|||||||
imports = imports,
|
imports = imports,
|
||||||
set_exports = set_exports,
|
set_exports = set_exports,
|
||||||
);
|
);
|
||||||
let wasm = self.module.emit_wasm().expect("failed to serialize");
|
let wasm = self.module.emit_wasm();
|
||||||
let (bytes, booted) = if self.base64 {
|
let (bytes, booted) = if self.base64 {
|
||||||
(
|
(
|
||||||
format!(
|
format!(
|
||||||
|
@ -55,8 +55,115 @@ use crate::webidl::{NonstandardIncoming, NonstandardOutgoing};
|
|||||||
use crate::webidl::{NonstandardWebidlSection, WasmBindgenAux};
|
use crate::webidl::{NonstandardWebidlSection, WasmBindgenAux};
|
||||||
use failure::{bail, Error, ResultExt};
|
use failure::{bail, Error, ResultExt};
|
||||||
use walrus::Module;
|
use walrus::Module;
|
||||||
|
use wasm_bindgen_multi_value_xform as multi_value_xform;
|
||||||
|
use wasm_bindgen_wasm_conventions as wasm_conventions;
|
||||||
use wasm_webidl_bindings::ast;
|
use wasm_webidl_bindings::ast;
|
||||||
|
|
||||||
|
pub fn add_multi_value(
|
||||||
|
module: &mut Module,
|
||||||
|
bindings: &mut NonstandardWebidlSection,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut to_xform = vec![];
|
||||||
|
for (id, binding) in &bindings.exports {
|
||||||
|
if let Some(ref results) = binding.return_via_outptr {
|
||||||
|
// LLVM currently always uses the first parameter for the return
|
||||||
|
// pointer. We hard code that here, since we have no better option.
|
||||||
|
let return_pointer_index = 0;
|
||||||
|
to_xform.push((*id, return_pointer_index, &results[..]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if to_xform.is_empty() {
|
||||||
|
// Early exit to avoid failing if we don't have a memory or shadow stack
|
||||||
|
// pointer because this is a minimal module that doesn't use linear
|
||||||
|
// memory.
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let shadow_stack_pointer = wasm_conventions::get_shadow_stack_pointer(module)?;
|
||||||
|
let memory = wasm_conventions::get_memory(module)?;
|
||||||
|
multi_value_xform::run(module, memory, shadow_stack_pointer, &to_xform)?;
|
||||||
|
|
||||||
|
// Finally, unset `return_via_outptr`, fix up its incoming bindings'
|
||||||
|
// argument numberings, and update its function type.
|
||||||
|
for (id, binding) in &mut bindings.exports {
|
||||||
|
if binding.return_via_outptr.take().is_some() {
|
||||||
|
if binding.incoming.is_empty() {
|
||||||
|
bail!("missing incoming binding expression for return pointer parameter");
|
||||||
|
}
|
||||||
|
if !is_ret_ptr_bindings(binding.incoming.remove(0)) {
|
||||||
|
bail!("unexpected incoming binding expression for return pointer parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
fixup_binding_argument_gets(&mut binding.incoming)?;
|
||||||
|
|
||||||
|
let func = match module.exports.get(*id).item {
|
||||||
|
walrus::ExportItem::Function(f) => f,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
binding.wasm_ty = module.funcs.get(func).ty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ret_ptr_bindings(b: NonstandardIncoming) -> bool {
|
||||||
|
match b {
|
||||||
|
NonstandardIncoming::Standard(ast::IncomingBindingExpression::As(
|
||||||
|
ast::IncomingBindingExpressionAs {
|
||||||
|
ty: walrus::ValType::I32,
|
||||||
|
expr,
|
||||||
|
},
|
||||||
|
)) => match *expr {
|
||||||
|
ast::IncomingBindingExpression::Get(ast::IncomingBindingExpressionGet { idx: 0 }) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we removed the first parameter (which was the return pointer) now all
|
||||||
|
// of the `Get` binding expression's are off by one. This function fixes these
|
||||||
|
// `Get`s.
|
||||||
|
fn fixup_binding_argument_gets(incoming: &mut [NonstandardIncoming]) -> Result<(), Error> {
|
||||||
|
for inc in incoming {
|
||||||
|
fixup_nonstandard_incoming(inc)?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
|
||||||
|
fn fixup_nonstandard_incoming(inc: &mut NonstandardIncoming) -> Result<(), Error> {
|
||||||
|
match inc {
|
||||||
|
NonstandardIncoming::Standard(s) => fixup_standard_incoming(s),
|
||||||
|
_ => bail!("found usage of non-standard bindings when in standard-bindings-only mode"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixup_standard_incoming(s: &mut ast::IncomingBindingExpression) -> Result<(), Error> {
|
||||||
|
match s {
|
||||||
|
ast::IncomingBindingExpression::Get(e) => {
|
||||||
|
if e.idx == 0 {
|
||||||
|
bail!(
|
||||||
|
"found usage of removed return pointer parameter in \
|
||||||
|
non-return pointer bindings"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
e.idx -= 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::IncomingBindingExpression::As(e) => fixup_standard_incoming(&mut e.expr),
|
||||||
|
ast::IncomingBindingExpression::AllocUtf8Str(e) => fixup_standard_incoming(&mut e.expr),
|
||||||
|
ast::IncomingBindingExpression::AllocCopy(e) => fixup_standard_incoming(&mut e.expr),
|
||||||
|
ast::IncomingBindingExpression::EnumToI32(e) => fixup_standard_incoming(&mut e.expr),
|
||||||
|
ast::IncomingBindingExpression::Field(e) => fixup_standard_incoming(&mut e.expr),
|
||||||
|
ast::IncomingBindingExpression::BindImport(e) => fixup_standard_incoming(&mut e.expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_section(
|
pub fn add_section(
|
||||||
module: &mut Module,
|
module: &mut Module,
|
||||||
aux: &WasmBindgenAux,
|
aux: &WasmBindgenAux,
|
||||||
@ -310,7 +417,7 @@ fn extract_incoming(
|
|||||||
exprs.push(e.clone());
|
exprs.push(e.clone());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
NonstandardIncoming::Int64 { .. } => "64-bit integer",
|
NonstandardIncoming::Int64 { .. } => "64-bit integer",
|
||||||
NonstandardIncoming::AllocCopyInt64 { .. } => "64-bit integer array",
|
NonstandardIncoming::AllocCopyInt64 { .. } => "64-bit integer array",
|
||||||
NonstandardIncoming::AllocCopyAnyrefArray { .. } => "array of JsValue",
|
NonstandardIncoming::AllocCopyAnyrefArray { .. } => "array of JsValue",
|
||||||
NonstandardIncoming::MutableSlice { .. } => "mutable slice",
|
NonstandardIncoming::MutableSlice { .. } => "mutable slice",
|
||||||
@ -329,7 +436,10 @@ fn extract_incoming(
|
|||||||
NonstandardIncoming::Char { .. } => "character",
|
NonstandardIncoming::Char { .. } => "character",
|
||||||
NonstandardIncoming::BorrowedAnyref { .. } => "borrowed anyref",
|
NonstandardIncoming::BorrowedAnyref { .. } => "borrowed anyref",
|
||||||
};
|
};
|
||||||
bail!("cannot represent {} with a standard bindings expression", desc);
|
bail!(
|
||||||
|
"cannot represent {} with a standard bindings expression",
|
||||||
|
desc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(exprs)
|
Ok(exprs)
|
||||||
}
|
}
|
||||||
@ -382,7 +492,10 @@ fn extract_outgoing(
|
|||||||
NonstandardOutgoing::OptionRustType { .. } => "optional rust type",
|
NonstandardOutgoing::OptionRustType { .. } => "optional rust type",
|
||||||
NonstandardOutgoing::StackClosure { .. } => "closures",
|
NonstandardOutgoing::StackClosure { .. } => "closures",
|
||||||
};
|
};
|
||||||
bail!("cannot represent {} with a standard bindings expression", desc);
|
bail!(
|
||||||
|
"cannot represent {} with a standard bindings expression",
|
||||||
|
desc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(exprs)
|
Ok(exprs)
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ rouille = { version = "3.0.0", default-features = false }
|
|||||||
serde = { version = "1.0", features = ['derive'] }
|
serde = { version = "1.0", features = ['derive'] }
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
walrus = { version = "0.11.0", features = ['parallel'] }
|
walrus = { version = "0.12.0", features = ['parallel'] }
|
||||||
wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.50" }
|
wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.50" }
|
||||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.50" }
|
wasm-bindgen-shared = { path = "../shared", version = "=0.2.50" }
|
||||||
|
|
||||||
|
16
crates/multi-value-xform/Cargo.toml
Normal file
16
crates/multi-value-xform/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm-bindgen-multi-value-xform"
|
||||||
|
version = "0.2.50"
|
||||||
|
authors = ["The wasm-bindgen Developers"]
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/multi-value-xform"
|
||||||
|
homepage = "https://rustwasm.github.io/wasm-bindgen/"
|
||||||
|
documentation = "https://docs.rs/wasm-bindgen-multi-value-xform"
|
||||||
|
description = """
|
||||||
|
Internal multi-value transformations for wasm-bindgen
|
||||||
|
"""
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = "0.1"
|
||||||
|
walrus = "0.12.0"
|
338
crates/multi-value-xform/src/lib.rs
Normal file
338
crates/multi-value-xform/src/lib.rs
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
//! The `wasm-bindgen` multi-value transformation.
|
||||||
|
//!
|
||||||
|
//! This crate provides a transformation to turn exported functions that use a
|
||||||
|
//! return pointer into exported functions that use multi-value.
|
||||||
|
//!
|
||||||
|
//! Consider the following function:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! #[no_mangle]
|
||||||
|
//! pub extern "C" fn pair(a: u32, b: u32) -> [u32; 2] {
|
||||||
|
//! [a, b]
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! LLVM will by default compile this down into the following Wasm:
|
||||||
|
//!
|
||||||
|
//! ```wasm
|
||||||
|
//! (func $pair (param i32 i32 i32)
|
||||||
|
//! local.get 0
|
||||||
|
//! local.get 2
|
||||||
|
//! i32.store offset=4
|
||||||
|
//! local.get 0
|
||||||
|
//! local.get 1
|
||||||
|
//! i32.store)
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! What's happening here is that the function is not directly returning the
|
||||||
|
//! pair at all, but instead the first `i32` parameter is a pointer to some
|
||||||
|
//! scratch space, and the return value is written into the scratch space. LLVM
|
||||||
|
//! does this because it doesn't yet have support for multi-value Wasm, and so
|
||||||
|
//! it only knows how to return a single value at a time.
|
||||||
|
//!
|
||||||
|
//! Ideally, with multi-value, what we would like instead is this:
|
||||||
|
//!
|
||||||
|
//! ```wasm
|
||||||
|
//! (func $pair (param i32 i32) (result i32 i32)
|
||||||
|
//! local.get 0
|
||||||
|
//! local.get 1)
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! However, that's not what this transformation does at the moment. This
|
||||||
|
//! transformation is a little simpler than mutating existing functions to
|
||||||
|
//! produce a multi-value result, instead it introduces new functions that wrap
|
||||||
|
//! the original function and translate the return pointer to multi-value
|
||||||
|
//! results in this wrapper function.
|
||||||
|
//!
|
||||||
|
//! With our running example, we end up with this:
|
||||||
|
//!
|
||||||
|
//! ```wasm
|
||||||
|
//! ;; The original function.
|
||||||
|
//! (func $pair (param i32 i32 i32)
|
||||||
|
//! local.get 0
|
||||||
|
//! local.get 2
|
||||||
|
//! i32.store offset=4
|
||||||
|
//! local.get 0
|
||||||
|
//! local.get 1
|
||||||
|
//! i32.store)
|
||||||
|
//!
|
||||||
|
//! (func $pairWrapper (param i32 i32) (result i32 i32)
|
||||||
|
//! ;; Our return pointer that points to the scratch space we are allocating
|
||||||
|
//! ;; on the shadow stack for calling `$pair`.
|
||||||
|
//! (local i32)
|
||||||
|
//!
|
||||||
|
//! ;; Allocate space on the shadow stack for the result.
|
||||||
|
//! global.get $shadowStackPointer
|
||||||
|
//! i32.const 8
|
||||||
|
//! i32.sub
|
||||||
|
//! local.tee 2
|
||||||
|
//! global.set $shadowStackPointer
|
||||||
|
//!
|
||||||
|
//! ;; Call `$pair` with our allocated shadow stack space for its results.
|
||||||
|
//! local.get 2
|
||||||
|
//! local.get 0
|
||||||
|
//! local.get 1
|
||||||
|
//! call $pair
|
||||||
|
//!
|
||||||
|
//! ;; Copy the return values from the shadow stack to the wasm stack.
|
||||||
|
//! local.get 2
|
||||||
|
//! i32.load
|
||||||
|
//! local.get 2 offset=4
|
||||||
|
//! i32.load
|
||||||
|
//!
|
||||||
|
//! ;; Finally, restore the shadow stack pointer.
|
||||||
|
//! local.get 2
|
||||||
|
//! i32.const 8
|
||||||
|
//! i32.add
|
||||||
|
//! global.set $shadowStackPointer)
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! This `$pairWrapper` function is what we actually end up exporting instead of
|
||||||
|
//! `$pair`.
|
||||||
|
|
||||||
|
#![deny(missing_docs, missing_debug_implementations)]
|
||||||
|
|
||||||
|
/// Run the transformation.
|
||||||
|
///
|
||||||
|
/// See the module-level docs for details on the transformation.
|
||||||
|
///
|
||||||
|
/// * `memory` is the module's memory that has the shadow stack where return
|
||||||
|
/// pointers are allocated within.
|
||||||
|
///
|
||||||
|
/// * `shadow_stack_pointer` is the global that is being used as the stack
|
||||||
|
/// pointer for the shadow stack. With LLVM, this is typically the first
|
||||||
|
/// global.
|
||||||
|
///
|
||||||
|
/// * `to_xform` is the set of exported functions we want to transform and
|
||||||
|
/// information required to transform them. The `usize` is the index of the
|
||||||
|
/// return pointer parameter that will be removed. The `Vec<walrus::ValType>`
|
||||||
|
/// is the new result type that will be returned directly instead of via the
|
||||||
|
/// return pointer.
|
||||||
|
pub fn run(
|
||||||
|
module: &mut walrus::Module,
|
||||||
|
memory: walrus::MemoryId,
|
||||||
|
shadow_stack_pointer: walrus::GlobalId,
|
||||||
|
to_xform: &[(walrus::ExportId, usize, &[walrus::ValType])],
|
||||||
|
) -> Result<(), failure::Error> {
|
||||||
|
for &(export, return_pointer_index, results) in to_xform {
|
||||||
|
xform_one(
|
||||||
|
module,
|
||||||
|
memory,
|
||||||
|
shadow_stack_pointer,
|
||||||
|
export,
|
||||||
|
return_pointer_index,
|
||||||
|
results,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that `n` is aligned to `align`, rounding up as necessary.
|
||||||
|
fn round_up_to_alignment(n: u32, align: u32) -> u32 {
|
||||||
|
debug_assert!(align.is_power_of_two());
|
||||||
|
(n + align - 1) & !(align - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xform_one(
|
||||||
|
module: &mut walrus::Module,
|
||||||
|
memory: walrus::MemoryId,
|
||||||
|
shadow_stack_pointer: walrus::GlobalId,
|
||||||
|
export: walrus::ExportId,
|
||||||
|
return_pointer_index: usize,
|
||||||
|
results: &[walrus::ValType],
|
||||||
|
) -> Result<(), failure::Error> {
|
||||||
|
if module.globals.get(shadow_stack_pointer).ty != walrus::ValType::I32 {
|
||||||
|
failure::bail!("shadow stack pointer global does not have type `i32`");
|
||||||
|
}
|
||||||
|
|
||||||
|
let func = match module.exports.get(export).item {
|
||||||
|
walrus::ExportItem::Function(f) => f,
|
||||||
|
_ => {
|
||||||
|
failure::bail!("can only multi-value transform exported functions, found non-function")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute the total size of all results, potentially with padding to ensure
|
||||||
|
// that each result is aligned.
|
||||||
|
let mut results_size = 0;
|
||||||
|
for ty in results {
|
||||||
|
results_size = match ty {
|
||||||
|
walrus::ValType::I32 | walrus::ValType::F32 => {
|
||||||
|
debug_assert_eq!(results_size % 4, 0);
|
||||||
|
results_size + 4
|
||||||
|
}
|
||||||
|
walrus::ValType::I64 | walrus::ValType::F64 => {
|
||||||
|
round_up_to_alignment(results_size, 8) + 8
|
||||||
|
}
|
||||||
|
walrus::ValType::V128 => round_up_to_alignment(results_size, 16) + 16,
|
||||||
|
walrus::ValType::Anyref => failure::bail!(
|
||||||
|
"cannot multi-value transform functions that return \
|
||||||
|
anyref, since they can't go into linear memory"
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Round up to 16-byte alignment, since that's what LLVM's emitted Wasm code
|
||||||
|
// seems to expect.
|
||||||
|
let results_size = round_up_to_alignment(results_size, 16);
|
||||||
|
|
||||||
|
let ty = module.funcs.get(func).ty();
|
||||||
|
let (ty_params, ty_results) = module.types.params_results(ty);
|
||||||
|
|
||||||
|
if !ty_results.is_empty() {
|
||||||
|
failure::bail!(
|
||||||
|
"can only multi-value transform functions that don't return any \
|
||||||
|
results (since they should be returned on the stack via a pointer)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match ty_params.get(return_pointer_index) {
|
||||||
|
Some(walrus::ValType::I32) => {}
|
||||||
|
None => failure::bail!("the return pointer parameter doesn't exist"),
|
||||||
|
Some(_) => failure::bail!("the return pointer parameter is not `i32`"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_params: Vec<_> = ty_params
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, ty)| {
|
||||||
|
if i == return_pointer_index {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ty)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// The locals for the function parameters.
|
||||||
|
let params: Vec<_> = new_params.iter().map(|ty| module.locals.add(*ty)).collect();
|
||||||
|
|
||||||
|
// A local to hold our shadow stack-allocated return pointer.
|
||||||
|
let return_pointer = module.locals.add(walrus::ValType::I32);
|
||||||
|
|
||||||
|
let mut wrapper = walrus::FunctionBuilder::new(&mut module.types, &new_params, results);
|
||||||
|
let mut body = wrapper.func_body();
|
||||||
|
|
||||||
|
// Allocate space in the shadow stack for the call.
|
||||||
|
body.global_get(shadow_stack_pointer)
|
||||||
|
.i32_const(results_size as i32)
|
||||||
|
.binop(walrus::ir::BinaryOp::I32Sub)
|
||||||
|
.local_tee(return_pointer)
|
||||||
|
.global_set(shadow_stack_pointer);
|
||||||
|
|
||||||
|
// Push the parameters for calling our wrapped function -- including the
|
||||||
|
// return pointer! -- on to the stack.
|
||||||
|
for (i, local) in params.iter().enumerate() {
|
||||||
|
if i == return_pointer_index {
|
||||||
|
body.local_get(return_pointer);
|
||||||
|
}
|
||||||
|
body.local_get(*local);
|
||||||
|
}
|
||||||
|
if return_pointer_index == params.len() {
|
||||||
|
body.local_get(return_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call our wrapped function.
|
||||||
|
body.call(func);
|
||||||
|
|
||||||
|
// Copy the return values from our shadow stack-allocated space and onto the
|
||||||
|
// Wasm stack.
|
||||||
|
let mut offset = 0;
|
||||||
|
for ty in results {
|
||||||
|
debug_assert!(offset < results_size);
|
||||||
|
body.local_get(return_pointer);
|
||||||
|
match ty {
|
||||||
|
walrus::ValType::I32 => {
|
||||||
|
debug_assert_eq!(offset % 4, 0);
|
||||||
|
body.load(
|
||||||
|
memory,
|
||||||
|
walrus::ir::LoadKind::I32 { atomic: false },
|
||||||
|
walrus::ir::MemArg { align: 4, offset },
|
||||||
|
);
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
walrus::ValType::I64 => {
|
||||||
|
offset = round_up_to_alignment(offset, 8);
|
||||||
|
body.load(
|
||||||
|
memory,
|
||||||
|
walrus::ir::LoadKind::I64 { atomic: false },
|
||||||
|
walrus::ir::MemArg { align: 8, offset },
|
||||||
|
);
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
walrus::ValType::F32 => {
|
||||||
|
debug_assert_eq!(offset % 4, 0);
|
||||||
|
body.load(
|
||||||
|
memory,
|
||||||
|
walrus::ir::LoadKind::F32,
|
||||||
|
walrus::ir::MemArg { align: 4, offset },
|
||||||
|
);
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
walrus::ValType::F64 => {
|
||||||
|
offset = round_up_to_alignment(offset, 8);
|
||||||
|
body.load(
|
||||||
|
memory,
|
||||||
|
walrus::ir::LoadKind::F64,
|
||||||
|
walrus::ir::MemArg { align: 8, offset },
|
||||||
|
);
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
walrus::ValType::V128 => {
|
||||||
|
offset = round_up_to_alignment(offset, 16);
|
||||||
|
body.load(
|
||||||
|
memory,
|
||||||
|
walrus::ir::LoadKind::V128,
|
||||||
|
walrus::ir::MemArg { align: 16, offset },
|
||||||
|
);
|
||||||
|
offset += 16;
|
||||||
|
}
|
||||||
|
walrus::ValType::Anyref => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, restore the shadow stack pointer.
|
||||||
|
body.local_get(return_pointer)
|
||||||
|
.i32_const(results_size as i32)
|
||||||
|
.binop(walrus::ir::BinaryOp::I32Add)
|
||||||
|
.global_set(shadow_stack_pointer);
|
||||||
|
|
||||||
|
let wrapper = wrapper.finish(params, &mut module.funcs);
|
||||||
|
|
||||||
|
// Replace the old export with our new multi-value wrapper for it!
|
||||||
|
match module.exports.get_mut(export).item {
|
||||||
|
walrus::ExportItem::Function(ref mut f) => *f = wrapper,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn round_up_to_alignment_works() {
|
||||||
|
for (n, align, expected) in vec![
|
||||||
|
(0, 1, 0),
|
||||||
|
(1, 1, 1),
|
||||||
|
(2, 1, 2),
|
||||||
|
(0, 2, 0),
|
||||||
|
(1, 2, 2),
|
||||||
|
(2, 2, 2),
|
||||||
|
(3, 2, 4),
|
||||||
|
(0, 4, 0),
|
||||||
|
(1, 4, 4),
|
||||||
|
(2, 4, 4),
|
||||||
|
(3, 4, 4),
|
||||||
|
(4, 4, 4),
|
||||||
|
(5, 4, 8),
|
||||||
|
] {
|
||||||
|
let actual = super::round_up_to_alignment(n, align);
|
||||||
|
println!(
|
||||||
|
"round_up_to_alignment(n = {}, align = {}) = {} (expected {})",
|
||||||
|
n, align, actual, expected
|
||||||
|
);
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,4 +13,5 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
walrus = "0.11.0"
|
walrus = "0.12.0"
|
||||||
|
wasm-bindgen-wasm-conventions = { path = "../wasm-conventions", version = "=0.2.50" }
|
||||||
|
@ -7,6 +7,7 @@ use failure::{bail, format_err, Error};
|
|||||||
use walrus::ir::Value;
|
use walrus::ir::Value;
|
||||||
use walrus::{DataId, FunctionId, InitExpr, ValType};
|
use walrus::{DataId, FunctionId, InitExpr, ValType};
|
||||||
use walrus::{ExportItem, GlobalId, GlobalKind, ImportKind, MemoryId, Module};
|
use walrus::{ExportItem, GlobalId, GlobalKind, ImportKind, MemoryId, Module};
|
||||||
|
use wasm_bindgen_wasm_conventions as wasm_conventions;
|
||||||
|
|
||||||
const PAGE_SIZE: u32 = 1 << 16;
|
const PAGE_SIZE: u32 = 1 << 16;
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ const PAGE_SIZE: u32 = 1 << 16;
|
|||||||
pub struct Config {
|
pub struct Config {
|
||||||
maximum_memory: u32,
|
maximum_memory: u32,
|
||||||
thread_stack_size: u32,
|
thread_stack_size: u32,
|
||||||
|
enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -24,9 +26,15 @@ impl Config {
|
|||||||
Config {
|
Config {
|
||||||
maximum_memory: 1 << 30, // 1GB
|
maximum_memory: 1 << 30, // 1GB
|
||||||
thread_stack_size: 1 << 20, // 1MB
|
thread_stack_size: 1 << 20, // 1MB
|
||||||
|
enabled: env::var("WASM_BINDGEN_THREADS").is_ok(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is threaded Wasm enabled?
|
||||||
|
pub fn is_enabled(&self) -> bool {
|
||||||
|
self.enabled
|
||||||
|
}
|
||||||
|
|
||||||
/// Specify the maximum amount of memory the wasm module can ever have.
|
/// Specify the maximum amount of memory the wasm module can ever have.
|
||||||
///
|
///
|
||||||
/// We'll be specifying that the memory for this wasm module is shared, and
|
/// We'll be specifying that the memory for this wasm module is shared, and
|
||||||
@ -79,18 +87,22 @@ impl Config {
|
|||||||
///
|
///
|
||||||
/// More and/or less may happen here over time, stay tuned!
|
/// More and/or less may happen here over time, stay tuned!
|
||||||
pub fn run(&self, module: &mut Module) -> Result<(), Error> {
|
pub fn run(&self, module: &mut Module) -> Result<(), Error> {
|
||||||
|
if !self.enabled {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// Compatibility with older LLVM outputs. Newer LLVM outputs, when
|
// Compatibility with older LLVM outputs. Newer LLVM outputs, when
|
||||||
// atomics are enabled, emit a shared memory. That's a good indicator
|
// atomics are enabled, emit a shared memory. That's a good indicator
|
||||||
// that we have work to do. If shared memory isn't enabled, though then
|
// that we have work to do. If shared memory isn't enabled, though then
|
||||||
// this isn't an atomic module so there's nothing to do. We still allow,
|
// this isn't an atomic module so there's nothing to do. We still allow,
|
||||||
// though, an environment variable to force us to go down this path to
|
// though, an environment variable to force us to go down this path to
|
||||||
// remain compatibile with older LLVM outputs.
|
// remain compatibile with older LLVM outputs.
|
||||||
let memory = find_memory(module)?;
|
let memory = wasm_conventions::get_memory(module)?;
|
||||||
if !module.memories.get(memory).shared && env::var("WASM_BINDGEN_THREADS").is_err() {
|
if !module.memories.get(memory).shared {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let stack_pointer = find_stack_pointer(module)?;
|
let stack_pointer = wasm_conventions::get_shadow_stack_pointer(module)?;
|
||||||
let addr = allocate_static_data(module, memory, 4, 4)?;
|
let addr = allocate_static_data(module, memory, 4, 4)?;
|
||||||
let zero = InitExpr::Value(Value::I32(0));
|
let zero = InitExpr::Value(Value::I32(0));
|
||||||
let globals = Globals {
|
let globals = Globals {
|
||||||
@ -207,17 +219,6 @@ fn switch_data_segments_to_passive(
|
|||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_memory(module: &mut Module) -> Result<MemoryId, Error> {
|
|
||||||
let mut memories = module.memories.iter();
|
|
||||||
let memory = memories
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| format_err!("currently incompatible with no memory modules"))?;
|
|
||||||
if memories.next().is_some() {
|
|
||||||
bail!("only one memory is currently supported");
|
|
||||||
}
|
|
||||||
Ok(memory.id())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_memory(module: &mut Module, memory: MemoryId, max: u32) -> Result<MemoryId, Error> {
|
fn update_memory(module: &mut Module, memory: MemoryId, max: u32) -> Result<MemoryId, Error> {
|
||||||
assert!(max % PAGE_SIZE == 0);
|
assert!(max % PAGE_SIZE == 0);
|
||||||
let memory = module.memories.get_mut(memory);
|
let memory = module.memories.get_mut(memory);
|
||||||
@ -313,37 +314,6 @@ fn allocate_static_data(
|
|||||||
Ok(address)
|
Ok(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_stack_pointer(module: &mut Module) -> Result<Option<GlobalId>, Error> {
|
|
||||||
let candidates = module
|
|
||||||
.globals
|
|
||||||
.iter()
|
|
||||||
.filter(|g| g.ty == ValType::I32)
|
|
||||||
.filter(|g| g.mutable)
|
|
||||||
.filter(|g| match g.kind {
|
|
||||||
GlobalKind::Local(_) => true,
|
|
||||||
GlobalKind::Import(_) => false,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if candidates.len() == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
if candidates.len() > 2 {
|
|
||||||
bail!("too many mutable globals to infer the stack pointer");
|
|
||||||
}
|
|
||||||
if candidates.len() == 1 {
|
|
||||||
return Ok(Some(candidates[0].id()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we've got two mutable globals then we're in a pretty standard
|
|
||||||
// situation for threaded code where one is the stack pointer and one is the
|
|
||||||
// TLS base offset. We need to figure out which is which, and we basically
|
|
||||||
// assume LLVM's current codegen where the first is the stack pointer.
|
|
||||||
//
|
|
||||||
// TODO: have an actual check here.
|
|
||||||
Ok(Some(candidates[0].id()))
|
|
||||||
}
|
|
||||||
|
|
||||||
enum InitMemory {
|
enum InitMemory {
|
||||||
Segments(Vec<PassiveSegment>),
|
Segments(Vec<PassiveSegment>),
|
||||||
Call {
|
Call {
|
||||||
@ -358,7 +328,7 @@ fn inject_start(
|
|||||||
memory_init: InitMemory,
|
memory_init: InitMemory,
|
||||||
globals: &Globals,
|
globals: &Globals,
|
||||||
addr: u32,
|
addr: u32,
|
||||||
stack_pointer: Option<GlobalId>,
|
stack_pointer: GlobalId,
|
||||||
stack_size: u32,
|
stack_size: u32,
|
||||||
memory: MemoryId,
|
memory: MemoryId,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@ -388,36 +358,33 @@ fn inject_start(
|
|||||||
// nonzero (assuming we don't overflow...)
|
// nonzero (assuming we don't overflow...)
|
||||||
body.local_get(local);
|
body.local_get(local);
|
||||||
body.if_else(
|
body.if_else(
|
||||||
Box::new([]),
|
None,
|
||||||
Box::new([]),
|
|
||||||
// If our thread id is nonzero then we're the second or greater thread, so
|
// If our thread id is nonzero then we're the second or greater thread, so
|
||||||
// we give ourselves a stack via memory.grow and we update our stack
|
// we give ourselves a stack via memory.grow and we update our stack
|
||||||
// pointer as the default stack pointer is surely wrong for us.
|
// pointer as the default stack pointer is surely wrong for us.
|
||||||
|body| {
|
|body| {
|
||||||
if let Some(stack_pointer) = stack_pointer {
|
// local0 = grow_memory(stack_size);
|
||||||
// local0 = grow_memory(stack_size);
|
body.i32_const((stack_size / PAGE_SIZE) as i32)
|
||||||
body.i32_const((stack_size / PAGE_SIZE) as i32)
|
.memory_grow(memory)
|
||||||
.memory_grow(memory)
|
.local_set(local);
|
||||||
.local_set(local);
|
|
||||||
|
|
||||||
// if local0 == -1 then trap
|
// if local0 == -1 then trap
|
||||||
body.block(Box::new([]), Box::new([]), |body| {
|
body.block(None, |body| {
|
||||||
let target = body.id();
|
let target = body.id();
|
||||||
body.local_get(local)
|
|
||||||
.i32_const(-1)
|
|
||||||
.binop(BinaryOp::I32Ne)
|
|
||||||
.br_if(target)
|
|
||||||
.unreachable();
|
|
||||||
});
|
|
||||||
|
|
||||||
// stack_pointer = local0 + stack_size
|
|
||||||
body.local_get(local)
|
body.local_get(local)
|
||||||
.i32_const(PAGE_SIZE as i32)
|
.i32_const(-1)
|
||||||
.binop(BinaryOp::I32Mul)
|
.binop(BinaryOp::I32Ne)
|
||||||
.i32_const(stack_size as i32)
|
.br_if(target)
|
||||||
.binop(BinaryOp::I32Add)
|
.unreachable();
|
||||||
.global_set(stack_pointer);
|
});
|
||||||
}
|
|
||||||
|
// stack_pointer = local0 + stack_size
|
||||||
|
body.local_get(local)
|
||||||
|
.i32_const(PAGE_SIZE as i32)
|
||||||
|
.binop(BinaryOp::I32Mul)
|
||||||
|
.i32_const(stack_size as i32)
|
||||||
|
.binop(BinaryOp::I32Add)
|
||||||
|
.global_set(stack_pointer);
|
||||||
},
|
},
|
||||||
// If the thread ID is zero then we can skip the update of the stack
|
// If the thread ID is zero then we can skip the update of the stack
|
||||||
// pointer as we know our stack pointer is valid. We need to initialize
|
// pointer as we know our stack pointer is valid. We need to initialize
|
||||||
|
16
crates/wasm-conventions/Cargo.toml
Normal file
16
crates/wasm-conventions/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm-bindgen-wasm-conventions"
|
||||||
|
version = "0.2.50"
|
||||||
|
authors = ["The wasm-bindgen developers"]
|
||||||
|
license = "MIT/Apache-2.0"
|
||||||
|
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/wasm-conventions"
|
||||||
|
homepage = "https://rustwasm.github.io/wasm-bindgen/"
|
||||||
|
documentation = "https://docs.rs/wasm-bindgen-wasm-conventions"
|
||||||
|
description = "Utilities for working with Wasm codegen conventions (usually established by LLVM/lld)"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
walrus = "0.12.0"
|
||||||
|
failure = "0.1.5"
|
95
crates/wasm-conventions/src/lib.rs
Executable file
95
crates/wasm-conventions/src/lib.rs
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
//! A tiny crate of utilities for working with implicit Wasm codegen conventions
|
||||||
|
//! (often established by LLVM and lld).
|
||||||
|
//!
|
||||||
|
//! Examples conventions include:
|
||||||
|
//!
|
||||||
|
//! * The shadow stack pointer
|
||||||
|
//! * The canonical linear memory that contains the shadow stack
|
||||||
|
|
||||||
|
#![deny(missing_docs, missing_debug_implementations)]
|
||||||
|
|
||||||
|
use failure::{bail, format_err, Error};
|
||||||
|
use walrus::{GlobalId, GlobalKind, MemoryId, Module, ValType};
|
||||||
|
|
||||||
|
/// Get a Wasm module's canonical linear memory.
|
||||||
|
pub fn get_memory(module: &Module) -> Result<MemoryId, Error> {
|
||||||
|
let mut memories = module.memories.iter().map(|m| m.id());
|
||||||
|
let memory = memories.next();
|
||||||
|
if memories.next().is_some() {
|
||||||
|
bail!(
|
||||||
|
"expected a single memory, found multiple; multiple memories \
|
||||||
|
currently not supported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
memory.ok_or_else(|| {
|
||||||
|
format_err!(
|
||||||
|
"module does not have a memory; must have a memory \
|
||||||
|
to transform return pointers into Wasm multi-value"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Discover the shadow stack pointer and add it to the module's exports as
|
||||||
|
/// `__shadow_stack_pointer`.
|
||||||
|
///
|
||||||
|
/// Adding it to the exports is useful for making sure it doesn't get GC'd.
|
||||||
|
pub fn export_shadow_stack_pointer(module: &mut Module) -> Result<(), Error> {
|
||||||
|
let candidates = module
|
||||||
|
.globals
|
||||||
|
.iter()
|
||||||
|
.filter(|g| g.ty == ValType::I32)
|
||||||
|
.filter(|g| g.mutable)
|
||||||
|
.filter(|g| match g.kind {
|
||||||
|
GlobalKind::Local(_) => true,
|
||||||
|
GlobalKind::Import(_) => false,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let ssp = match candidates.len() {
|
||||||
|
0 => bail!("could not find the shadow stack pointer for the module"),
|
||||||
|
// If we've got two mutable globals then we're in a pretty standard
|
||||||
|
// situation for threaded code where one is the stack pointer and one is the
|
||||||
|
// TLS base offset. We need to figure out which is which, and we basically
|
||||||
|
// assume LLVM's current codegen where the first is the stack pointer.
|
||||||
|
//
|
||||||
|
// TODO: have an actual check here.
|
||||||
|
1 | 2 => candidates[0].id(),
|
||||||
|
_ => bail!("too many mutable globals to infer which is the shadow stack pointer"),
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.add("__shadow_stack_pointer", ssp);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unexport the shadow stack pointer that was previously added to the module's
|
||||||
|
/// exports as `__shadow_stack_pointer`.
|
||||||
|
pub fn unexport_shadow_stack_pointer(module: &mut Module) -> Result<(), Error> {
|
||||||
|
let e = module
|
||||||
|
.exports
|
||||||
|
.iter()
|
||||||
|
.find(|e| e.name == "__shadow_stack_pointer")
|
||||||
|
.map(|e| e.id())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format_err!("did not find the `__shadow_stack_pointer` export in the module")
|
||||||
|
})?;
|
||||||
|
module.exports.delete(e);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `__shadow_stack_pointer`.
|
||||||
|
///
|
||||||
|
/// It must have been previously added to the module's exports via
|
||||||
|
/// `export_shadow_stack_pointer`.
|
||||||
|
pub fn get_shadow_stack_pointer(module: &Module) -> Result<GlobalId, Error> {
|
||||||
|
module
|
||||||
|
.exports
|
||||||
|
.iter()
|
||||||
|
.find(|e| e.name == "__shadow_stack_pointer")
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format_err!("did not find the `__shadow_stack_pointer` export in the module")
|
||||||
|
})
|
||||||
|
.and_then(|e| match e.item {
|
||||||
|
walrus::ExportItem::Global(g) => Ok(g),
|
||||||
|
_ => bail!("`__shadow_stack_pointer` export is wrong kind"),
|
||||||
|
})
|
||||||
|
}
|
@ -14,7 +14,7 @@ edition = '2018'
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
walrus = "0.11.0"
|
walrus = "0.12.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
53
publish.rs
53
publish.rs
@ -26,7 +26,9 @@ const CRATES_TO_PUBLISH: &[&str] = &[
|
|||||||
"wasm-bindgen-test",
|
"wasm-bindgen-test",
|
||||||
"wasm-bindgen-wasm-interpreter",
|
"wasm-bindgen-wasm-interpreter",
|
||||||
"wasm-bindgen-webidl",
|
"wasm-bindgen-webidl",
|
||||||
|
"wasm-bindgen-wasm-conventions",
|
||||||
"wasm-bindgen-threads-xform",
|
"wasm-bindgen-threads-xform",
|
||||||
|
"wasm-bindgen-multi-value-xform",
|
||||||
"wasm-bindgen-anyref-xform",
|
"wasm-bindgen-anyref-xform",
|
||||||
"wasm-bindgen-cli-support",
|
"wasm-bindgen-cli-support",
|
||||||
"wasm-bindgen-cli",
|
"wasm-bindgen-cli",
|
||||||
@ -39,7 +41,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[
|
|||||||
const CRATES_TO_AVOID_PUBLISH: &[&str] = &[
|
const CRATES_TO_AVOID_PUBLISH: &[&str] = &[
|
||||||
// We'll publish these when they're ready one day
|
// We'll publish these when they're ready one day
|
||||||
"wasm-bindgen-typescript",
|
"wasm-bindgen-typescript",
|
||||||
|
|
||||||
// These are internal crates, unlikely to ever be published
|
// These are internal crates, unlikely to ever be published
|
||||||
"ui-tests",
|
"ui-tests",
|
||||||
"sample",
|
"sample",
|
||||||
@ -60,7 +61,8 @@ fn main() {
|
|||||||
find_crates("crates".as_ref(), &mut crates);
|
find_crates("crates".as_ref(), &mut crates);
|
||||||
find_crates("examples".as_ref(), &mut crates);
|
find_crates("examples".as_ref(), &mut crates);
|
||||||
|
|
||||||
let pos = CRATES_TO_PUBLISH.iter()
|
let pos = CRATES_TO_PUBLISH
|
||||||
|
.iter()
|
||||||
.chain(CRATES_TO_AVOID_PUBLISH)
|
.chain(CRATES_TO_AVOID_PUBLISH)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, c)| (*c, i))
|
.map(|(i, c)| (*c, i))
|
||||||
@ -87,7 +89,8 @@ fn main() {
|
|||||||
fn find_crates(dir: &Path, dst: &mut Vec<Crate>) {
|
fn find_crates(dir: &Path, dst: &mut Vec<Crate>) {
|
||||||
if dir.join("Cargo.toml").exists() {
|
if dir.join("Cargo.toml").exists() {
|
||||||
let krate = read_crate(&dir.join("Cargo.toml"));
|
let krate = read_crate(&dir.join("Cargo.toml"));
|
||||||
if CRATES_TO_PUBLISH.iter()
|
if CRATES_TO_PUBLISH
|
||||||
|
.iter()
|
||||||
.chain(CRATES_TO_AVOID_PUBLISH)
|
.chain(CRATES_TO_AVOID_PUBLISH)
|
||||||
.any(|c| krate.name == *c)
|
.any(|c| krate.name == *c)
|
||||||
{
|
{
|
||||||
@ -112,16 +115,20 @@ fn read_crate(manifest: &Path) -> Crate {
|
|||||||
let mut version = None;
|
let mut version = None;
|
||||||
for line in fs::read_to_string(manifest).unwrap().lines() {
|
for line in fs::read_to_string(manifest).unwrap().lines() {
|
||||||
if name.is_none() && line.starts_with("name = \"") {
|
if name.is_none() && line.starts_with("name = \"") {
|
||||||
name = Some(line.replace("name = \"", "")
|
name = Some(
|
||||||
.replace("\"", "")
|
line.replace("name = \"", "")
|
||||||
.trim()
|
.replace("\"", "")
|
||||||
.to_string());
|
.trim()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if version.is_none() && line.starts_with("version = \"") {
|
if version.is_none() && line.starts_with("version = \"") {
|
||||||
version = Some(line.replace("version = \"", "")
|
version = Some(
|
||||||
.replace("\"", "")
|
line.replace("version = \"", "")
|
||||||
.trim()
|
.replace("\"", "")
|
||||||
.to_string());
|
.trim()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let name = name.unwrap();
|
let name = name.unwrap();
|
||||||
@ -148,10 +155,10 @@ fn bump_version(krate: &Crate, crates: &[Crate]) {
|
|||||||
let mut rewritten = false;
|
let mut rewritten = false;
|
||||||
if line.starts_with("version =") {
|
if line.starts_with("version =") {
|
||||||
if CRATES_TO_PUBLISH.contains(&&krate.name[..]) {
|
if CRATES_TO_PUBLISH.contains(&&krate.name[..]) {
|
||||||
println!("bump `{}` {} => {}",
|
println!(
|
||||||
krate.name,
|
"bump `{}` {} => {}",
|
||||||
krate.version,
|
krate.name, krate.version, krate.next_version
|
||||||
krate.next_version);
|
);
|
||||||
new_manifest.push_str(&line.replace(&krate.version, &krate.next_version));
|
new_manifest.push_str(&line.replace(&krate.version, &krate.next_version));
|
||||||
rewritten = true;
|
rewritten = true;
|
||||||
}
|
}
|
||||||
@ -165,20 +172,20 @@ fn bump_version(krate: &Crate, crates: &[Crate]) {
|
|||||||
|
|
||||||
for other in crates {
|
for other in crates {
|
||||||
if !is_deps || !line.starts_with(&format!("{} ", other.name)) {
|
if !is_deps || !line.starts_with(&format!("{} ", other.name)) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
if !line.contains(&other.version) {
|
if !line.contains(&other.version) {
|
||||||
if !line.contains("version =") {
|
if !line.contains("version =") {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
panic!("{:?} has a dep on {} but doesn't list version {}",
|
panic!(
|
||||||
krate.manifest,
|
"{:?} has a dep on {} but doesn't list version {}",
|
||||||
other.name,
|
krate.manifest, other.name, other.version
|
||||||
other.version);
|
);
|
||||||
}
|
}
|
||||||
rewritten = true;
|
rewritten = true;
|
||||||
new_manifest.push_str(&line.replace(&other.version, &other.next_version));
|
new_manifest.push_str(&line.replace(&other.version, &other.next_version));
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
if !rewritten {
|
if !rewritten {
|
||||||
new_manifest.push_str(line);
|
new_manifest.push_str(line);
|
||||||
@ -198,7 +205,7 @@ fn bump(version: &str) -> String {
|
|||||||
|
|
||||||
fn publish(krate: &Crate) {
|
fn publish(krate: &Crate) {
|
||||||
if !CRATES_TO_PUBLISH.iter().any(|s| *s == krate.name) {
|
if !CRATES_TO_PUBLISH.iter().any(|s| *s == krate.name) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if krate.name == "wasm-bindgen" {
|
if krate.name == "wasm-bindgen" {
|
||||||
println!("ABOUT TO PUBLISH wasm-bindgen");
|
println!("ABOUT TO PUBLISH wasm-bindgen");
|
||||||
|
@ -275,6 +275,7 @@ impl OptionFromWasmAbi for char {
|
|||||||
impl<T> IntoWasmAbi for *const T {
|
impl<T> IntoWasmAbi for *const T {
|
||||||
type Abi = u32;
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn into_abi(self) -> u32 {
|
fn into_abi(self) -> u32 {
|
||||||
self as u32
|
self as u32
|
||||||
}
|
}
|
||||||
@ -283,6 +284,7 @@ impl<T> IntoWasmAbi for *const T {
|
|||||||
impl<T> FromWasmAbi for *const T {
|
impl<T> FromWasmAbi for *const T {
|
||||||
type Abi = u32;
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
unsafe fn from_abi(js: u32) -> *const T {
|
unsafe fn from_abi(js: u32) -> *const T {
|
||||||
js as *const T
|
js as *const T
|
||||||
}
|
}
|
||||||
@ -291,6 +293,7 @@ impl<T> FromWasmAbi for *const T {
|
|||||||
impl<T> IntoWasmAbi for *mut T {
|
impl<T> IntoWasmAbi for *mut T {
|
||||||
type Abi = u32;
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn into_abi(self) -> u32 {
|
fn into_abi(self) -> u32 {
|
||||||
self as u32
|
self as u32
|
||||||
}
|
}
|
||||||
@ -299,6 +302,7 @@ impl<T> IntoWasmAbi for *mut T {
|
|||||||
impl<T> FromWasmAbi for *mut T {
|
impl<T> FromWasmAbi for *mut T {
|
||||||
type Abi = u32;
|
type Abi = u32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
unsafe fn from_abi(js: u32) -> *mut T {
|
unsafe fn from_abi(js: u32) -> *mut T {
|
||||||
js as *mut T
|
js as *mut T
|
||||||
}
|
}
|
||||||
@ -346,6 +350,7 @@ impl RefFromWasmAbi for JsValue {
|
|||||||
impl<T: OptionIntoWasmAbi> IntoWasmAbi for Option<T> {
|
impl<T: OptionIntoWasmAbi> IntoWasmAbi for Option<T> {
|
||||||
type Abi = T::Abi;
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn into_abi(self) -> T::Abi {
|
fn into_abi(self) -> T::Abi {
|
||||||
match self {
|
match self {
|
||||||
None => T::none(),
|
None => T::none(),
|
||||||
@ -357,6 +362,7 @@ impl<T: OptionIntoWasmAbi> IntoWasmAbi for Option<T> {
|
|||||||
impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
|
impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
|
||||||
type Abi = T::Abi;
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
unsafe fn from_abi(js: T::Abi) -> Self {
|
unsafe fn from_abi(js: T::Abi) -> Self {
|
||||||
if T::is_none(&js) {
|
if T::is_none(&js) {
|
||||||
None
|
None
|
||||||
@ -369,6 +375,7 @@ impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
|
|||||||
impl<T: IntoWasmAbi> IntoWasmAbi for Clamped<T> {
|
impl<T: IntoWasmAbi> IntoWasmAbi for Clamped<T> {
|
||||||
type Abi = T::Abi;
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn into_abi(self) -> Self::Abi {
|
fn into_abi(self) -> Self::Abi {
|
||||||
self.0.into_abi()
|
self.0.into_abi()
|
||||||
}
|
}
|
||||||
@ -377,6 +384,7 @@ impl<T: IntoWasmAbi> IntoWasmAbi for Clamped<T> {
|
|||||||
impl<T: FromWasmAbi> FromWasmAbi for Clamped<T> {
|
impl<T: FromWasmAbi> FromWasmAbi for Clamped<T> {
|
||||||
type Abi = T::Abi;
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
unsafe fn from_abi(js: T::Abi) -> Self {
|
unsafe fn from_abi(js: T::Abi) -> Self {
|
||||||
Clamped(T::from_abi(js))
|
Clamped(T::from_abi(js))
|
||||||
}
|
}
|
||||||
@ -394,6 +402,7 @@ impl IntoWasmAbi for () {
|
|||||||
impl<T: IntoWasmAbi> ReturnWasmAbi for Result<T, JsValue> {
|
impl<T: IntoWasmAbi> ReturnWasmAbi for Result<T, JsValue> {
|
||||||
type Abi = T::Abi;
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn return_abi(self) -> Self::Abi {
|
fn return_abi(self) -> Self::Abi {
|
||||||
match self {
|
match self {
|
||||||
Ok(v) => v.into_abi(),
|
Ok(v) => v.into_abi(),
|
||||||
|
@ -45,6 +45,7 @@ macro_rules! vectors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OptionIntoWasmAbi for Box<[$t]> {
|
impl OptionIntoWasmAbi for Box<[$t]> {
|
||||||
|
#[inline]
|
||||||
fn none() -> WasmSlice { null_slice() }
|
fn none() -> WasmSlice { null_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ macro_rules! vectors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OptionFromWasmAbi for Box<[$t]> {
|
impl OptionFromWasmAbi for Box<[$t]> {
|
||||||
|
#[inline]
|
||||||
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,6 +79,7 @@ macro_rules! vectors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OptionIntoWasmAbi for &'a [$t] {
|
impl<'a> OptionIntoWasmAbi for &'a [$t] {
|
||||||
|
#[inline]
|
||||||
fn none() -> WasmSlice { null_slice() }
|
fn none() -> WasmSlice { null_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +93,7 @@ macro_rules! vectors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
|
impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
|
||||||
|
#[inline]
|
||||||
fn none() -> WasmSlice { null_slice() }
|
fn none() -> WasmSlice { null_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,24 +148,28 @@ if_std! {
|
|||||||
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
||||||
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
|
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn into_abi(self) -> Self::Abi {
|
fn into_abi(self) -> Self::Abi {
|
||||||
self.into_boxed_slice().into_abi()
|
self.into_boxed_slice().into_abi()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OptionIntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
impl<T> OptionIntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
||||||
|
#[inline]
|
||||||
fn none() -> WasmSlice { null_slice() }
|
fn none() -> WasmSlice { null_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
||||||
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
|
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
unsafe fn from_abi(js: Self::Abi) -> Self {
|
unsafe fn from_abi(js: Self::Abi) -> Self {
|
||||||
<Box<[T]>>::from_abi(js).into()
|
<Box<[T]>>::from_abi(js).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OptionFromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
impl<T> OptionFromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
||||||
|
#[inline]
|
||||||
fn is_none(abi: &WasmSlice) -> bool { abi.ptr == 0 }
|
fn is_none(abi: &WasmSlice) -> bool { abi.ptr == 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +185,7 @@ if_std! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OptionIntoWasmAbi for String {
|
impl OptionIntoWasmAbi for String {
|
||||||
|
#[inline]
|
||||||
fn none() -> Self::Abi { null_slice() }
|
fn none() -> Self::Abi { null_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +199,7 @@ if_std! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OptionFromWasmAbi for String {
|
impl OptionFromWasmAbi for String {
|
||||||
|
#[inline]
|
||||||
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,6 +216,7 @@ impl<'a> IntoWasmAbi for &'a str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OptionIntoWasmAbi for &'a str {
|
impl<'a> OptionIntoWasmAbi for &'a str {
|
||||||
|
#[inline]
|
||||||
fn none() -> Self::Abi {
|
fn none() -> Self::Abi {
|
||||||
null_slice()
|
null_slice()
|
||||||
}
|
}
|
||||||
@ -240,6 +251,7 @@ if_std! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OptionIntoWasmAbi for Box<[JsValue]> {
|
impl OptionIntoWasmAbi for Box<[JsValue]> {
|
||||||
|
#[inline]
|
||||||
fn none() -> WasmSlice { null_slice() }
|
fn none() -> WasmSlice { null_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,6 +267,7 @@ if_std! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OptionFromWasmAbi for Box<[JsValue]> {
|
impl OptionFromWasmAbi for Box<[JsValue]> {
|
||||||
|
#[inline]
|
||||||
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,8 @@ pub trait ReturnWasmAbi: WasmDescribe {
|
|||||||
|
|
||||||
impl<T: IntoWasmAbi> ReturnWasmAbi for T {
|
impl<T: IntoWasmAbi> ReturnWasmAbi for T {
|
||||||
type Abi = T::Abi;
|
type Abi = T::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn return_abi(self) -> Self::Abi {
|
fn return_abi(self) -> Self::Abi {
|
||||||
self.into_abi()
|
self.into_abi()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user