mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-13 13:01:22 +00:00
Add test for consuming interface types inputs (#1900)
This commit adds a test suite for consuming interface types modules as input and producing a JS polyfill output. The tests are relatively simple today and don't exercise a ton of functionality, but they should hopefully cover the breadth of at least some basics of what wasm interface types supports today. A few small fixes were applied along the way, such as: * Don't require modules to have a stack pointer * Allow passing `*.wat`, `*.wit`, or `*.wasm` files as input to `wasm-bindgen` instead of always requiring `*.wasm`.
This commit is contained in:
@ -92,6 +92,9 @@ jobs:
|
|||||||
# - template: ci/azure-install-sccache.yml
|
# - 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"
|
||||||
|
- task: NodeTool@0
|
||||||
|
inputs:
|
||||||
|
versionSpec: '>=13.0'
|
||||||
- script: cargo test -p wasm-bindgen-cli-support
|
- script: cargo test -p wasm-bindgen-cli-support
|
||||||
displayName: "wasm-bindgen-cli-support tests"
|
displayName: "wasm-bindgen-cli-support tests"
|
||||||
- script: cargo test -p wasm-bindgen-cli
|
- script: cargo test -p wasm-bindgen-cli
|
||||||
|
@ -12,17 +12,19 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
|
|||||||
edition = '2018'
|
edition = '2018'
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.9"
|
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
base64 = "0.9"
|
||||||
log = "0.4"
|
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.14.0"
|
walrus = "0.14.0"
|
||||||
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.55' }
|
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.55' }
|
||||||
wasm-bindgen-shared = { path = "../shared", version = '=0.2.55' }
|
|
||||||
wasm-bindgen-multi-value-xform = { path = '../multi-value-xform', version = '=0.2.55' }
|
wasm-bindgen-multi-value-xform = { path = '../multi-value-xform', version = '=0.2.55' }
|
||||||
|
wasm-bindgen-shared = { path = "../shared", version = '=0.2.55' }
|
||||||
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.55' }
|
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.55' }
|
||||||
wasm-bindgen-wasm-conventions = { path = '../wasm-conventions', version = '=0.2.55' }
|
wasm-bindgen-wasm-conventions = { path = '../wasm-conventions', version = '=0.2.55' }
|
||||||
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.55' }
|
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.55' }
|
||||||
|
wit-text = "0.1.1"
|
||||||
wit-walrus = "0.1.0"
|
wit-walrus = "0.1.0"
|
||||||
|
wit-validator = "0.1.0"
|
||||||
|
@ -264,8 +264,10 @@ impl Bindgen {
|
|||||||
(mem::replace(m, blank_module), &name[..])
|
(mem::replace(m, blank_module), &name[..])
|
||||||
}
|
}
|
||||||
Input::Path(ref path) => {
|
Input::Path(ref path) => {
|
||||||
let contents = fs::read(&path)
|
let wasm = wit_text::parse_file(&path)
|
||||||
.with_context(|| format!("failed to read `{}`", path.display()))?;
|
.with_context(|| format!("failed to read `{}`", path.display()))?;
|
||||||
|
wit_validator::validate(&wasm)
|
||||||
|
.with_context(|| format!("failed to validate `{}`", path.display()))?;
|
||||||
let module = walrus::ModuleConfig::new()
|
let module = walrus::ModuleConfig::new()
|
||||||
// Skip validation of the module as LLVM's output is
|
// Skip validation of the module as LLVM's output is
|
||||||
// generally already well-formed and so we won't gain much
|
// generally already well-formed and so we won't gain much
|
||||||
@ -278,7 +280,7 @@ impl Bindgen {
|
|||||||
.generate_name_section(!self.remove_name_section)
|
.generate_name_section(!self.remove_name_section)
|
||||||
.generate_producers_section(!self.remove_producers_section)
|
.generate_producers_section(!self.remove_producers_section)
|
||||||
.on_parse(wit_walrus::on_parse)
|
.on_parse(wit_walrus::on_parse)
|
||||||
.parse(&contents)
|
.parse(&wasm)
|
||||||
.context("failed to parse input file as wasm")?;
|
.context("failed to parse input file as wasm")?;
|
||||||
let stem = match &self.out_name {
|
let stem = match &self.out_name {
|
||||||
Some(name) => &name,
|
Some(name) => &name,
|
||||||
|
@ -92,8 +92,8 @@ pub fn process(
|
|||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
fn init(&mut self) -> Result<(), Error> {
|
fn init(&mut self) -> Result<(), Error> {
|
||||||
let stack_pointer = wasm_bindgen_wasm_conventions::get_shadow_stack_pointer(self.module)?;
|
self.aux.shadow_stack_pointer =
|
||||||
self.aux.shadow_stack_pointer = Some(stack_pointer);
|
wasm_bindgen_wasm_conventions::get_shadow_stack_pointer(self.module);
|
||||||
|
|
||||||
// Make a map from string name to ids of all exports
|
// Make a map from string name to ids of all exports
|
||||||
for export in self.module.exports.iter() {
|
for export in self.module.exports.iter() {
|
||||||
|
@ -36,6 +36,7 @@ rayon = "1.0"
|
|||||||
tempfile = "3.0"
|
tempfile = "3.0"
|
||||||
walrus = "0.14"
|
walrus = "0.14"
|
||||||
wit-printer = "0.1"
|
wit-printer = "0.1"
|
||||||
|
wit-text = "0.1"
|
||||||
wit-validator = "0.1"
|
wit-validator = "0.1"
|
||||||
wit-walrus = "0.1"
|
wit-walrus = "0.1"
|
||||||
|
|
||||||
@ -43,5 +44,9 @@ wit-walrus = "0.1"
|
|||||||
name = "reference"
|
name = "reference"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "interface-types"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
vendored-openssl = ['openssl/vendored']
|
vendored-openssl = ['openssl/vendored']
|
||||||
|
85
crates/cli/tests/interface-types.rs
Normal file
85
crates/cli/tests/interface-types.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
use assert_cmd::prelude::*;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let filter = env::args().nth(1);
|
||||||
|
|
||||||
|
let mut tests = Vec::new();
|
||||||
|
let dir = env::current_dir()?.join("tests/interface-types");
|
||||||
|
for entry in dir.read_dir()? {
|
||||||
|
let path = entry?.path();
|
||||||
|
if path.extension().and_then(|s| s.to_str()) != Some("wit") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(filter) = &filter {
|
||||||
|
if !path.display().to_string().contains(filter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tests.push(path);
|
||||||
|
}
|
||||||
|
tests.sort();
|
||||||
|
|
||||||
|
let errs = tests
|
||||||
|
.par_iter()
|
||||||
|
.filter_map(|t| runtest(t).err().map(|e| (t, e)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if errs.len() == 0 {
|
||||||
|
println!("{} tests passed", tests.len());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
eprintln!("failed tests:\n");
|
||||||
|
for (test, err) in errs {
|
||||||
|
eprintln!("{} failure\n{}", test.display(), tab(&format!("{:?}", err)));
|
||||||
|
}
|
||||||
|
bail!("tests failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtest(test: &Path) -> Result<()> {
|
||||||
|
let js = test.with_extension("js");
|
||||||
|
let td = tempfile::TempDir::new()?;
|
||||||
|
|
||||||
|
let mut bindgen = Command::cargo_bin("wasm-bindgen")?;
|
||||||
|
bindgen
|
||||||
|
.arg("--out-dir")
|
||||||
|
.arg(td.path())
|
||||||
|
.arg(test)
|
||||||
|
.arg("--out-name=wasm")
|
||||||
|
.arg("--nodejs")
|
||||||
|
.arg("--no-typescript");
|
||||||
|
exec(&mut bindgen)?;
|
||||||
|
|
||||||
|
exec(Command::new("node")
|
||||||
|
.arg("--experimental-wasm-anyref")
|
||||||
|
.arg("--experimental-wasm-mv")
|
||||||
|
.arg(&js).env("NODE_PATH", td.path()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec(cmd: &mut Command) -> Result<()> {
|
||||||
|
let output = cmd.output()?;
|
||||||
|
if output.status.success() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut err = format!("command failed {:?}", cmd);
|
||||||
|
err.push_str(&format!("\nstatus: {}", output.status));
|
||||||
|
err.push_str(&format!(
|
||||||
|
"\nstderr:\n{}",
|
||||||
|
tab(&String::from_utf8_lossy(&output.stderr))
|
||||||
|
));
|
||||||
|
err.push_str(&format!(
|
||||||
|
"\nstdout:\n{}",
|
||||||
|
tab(&String::from_utf8_lossy(&output.stdout))
|
||||||
|
));
|
||||||
|
bail!("{}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab(s: &str) -> String {
|
||||||
|
format!(" {}", s.replace("\n", "\n "))
|
||||||
|
}
|
16
crates/cli/tests/interface-types/anyref.js
Normal file
16
crates/cli/tests/interface-types/anyref.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
const wasm = require('wasm');
|
||||||
|
|
||||||
|
const obj = {};
|
||||||
|
assert.strictEqual(wasm.foo(obj), obj);
|
||||||
|
|
||||||
|
wasm.store('x');
|
||||||
|
assert.strictEqual(wasm.load(), 'x');
|
||||||
|
|
||||||
|
const obj2 = {};
|
||||||
|
wasm.store(obj2);
|
||||||
|
assert.strictEqual(wasm.load(), obj2);
|
||||||
|
assert.strictEqual(wasm.load(), obj2);
|
||||||
|
|
||||||
|
wasm.store(undefined);
|
||||||
|
assert.strictEqual(wasm.load(), undefined);
|
26
crates/cli/tests/interface-types/anyref.wit
Normal file
26
crates/cli/tests/interface-types/anyref.wit
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
(module
|
||||||
|
(func $foo (param anyref) (result anyref)
|
||||||
|
local.get 0)
|
||||||
|
|
||||||
|
(func $store (param anyref)
|
||||||
|
i32.const 0
|
||||||
|
local.get 0
|
||||||
|
table.set 0)
|
||||||
|
|
||||||
|
(func $load (result anyref)
|
||||||
|
i32.const 0
|
||||||
|
table.get 0)
|
||||||
|
|
||||||
|
(table 1 anyref)
|
||||||
|
|
||||||
|
(@interface func (export "foo") (param anyref) (result anyref)
|
||||||
|
arg.get 0
|
||||||
|
call-core $foo)
|
||||||
|
|
||||||
|
(@interface func (export "store") (param anyref)
|
||||||
|
arg.get 0
|
||||||
|
call-core $store)
|
||||||
|
|
||||||
|
(@interface func (export "load") (result anyref)
|
||||||
|
call-core $load)
|
||||||
|
)
|
5
crates/cli/tests/interface-types/defer-call.js
Normal file
5
crates/cli/tests/interface-types/defer-call.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const wasm = require('wasm');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
assert.strictEqual(wasm.foo(), 0);
|
||||||
|
assert.strictEqual(wasm.get(), 1);
|
21
crates/cli/tests/interface-types/defer-call.wit
Normal file
21
crates/cli/tests/interface-types/defer-call.wit
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
(module
|
||||||
|
(global $ctr (mut i32) (i32.const 0))
|
||||||
|
|
||||||
|
(func $increment
|
||||||
|
global.get $ctr
|
||||||
|
i32.const 1
|
||||||
|
i32.add
|
||||||
|
global.set $ctr)
|
||||||
|
|
||||||
|
(func $get (result i32)
|
||||||
|
global.get $ctr)
|
||||||
|
|
||||||
|
(@interface func (export "foo") (result s32)
|
||||||
|
defer-call-core $increment
|
||||||
|
call-core $get
|
||||||
|
i32-to-s32)
|
||||||
|
|
||||||
|
(@interface func (export "get") (result s32)
|
||||||
|
call-core $get
|
||||||
|
i32-to-s32)
|
||||||
|
)
|
2
crates/cli/tests/interface-types/empty.js
Normal file
2
crates/cli/tests/interface-types/empty.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
const m = require('wasm');
|
||||||
|
|
1
crates/cli/tests/interface-types/empty.wit
Normal file
1
crates/cli/tests/interface-types/empty.wit
Normal file
@ -0,0 +1 @@
|
|||||||
|
(module)
|
9
crates/cli/tests/interface-types/integers.js
Normal file
9
crates/cli/tests/interface-types/integers.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
const wasm = require('wasm');
|
||||||
|
|
||||||
|
assert.strictEqual(wasm.add_i8(0, 1), 1);
|
||||||
|
assert.strictEqual(wasm.add_u8(0, 1), 1);
|
||||||
|
assert.strictEqual(wasm.add_i16(0, 1), 1);
|
||||||
|
assert.strictEqual(wasm.add_u16(0, 1), 1);
|
||||||
|
assert.strictEqual(wasm.add_i32(0, 1), 1);
|
||||||
|
assert.strictEqual(wasm.add_u32(0, 1), 1);
|
54
crates/cli/tests/interface-types/integers.wit
Normal file
54
crates/cli/tests/interface-types/integers.wit
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
(module
|
||||||
|
(func $add_i32 (param i32 i32) (result i32)
|
||||||
|
local.get 0
|
||||||
|
local.get 1
|
||||||
|
i32.add)
|
||||||
|
|
||||||
|
(@interface func (export "add_i8") (param s8) (param s8) (result s8)
|
||||||
|
arg.get 0
|
||||||
|
s8-to-i32
|
||||||
|
arg.get 1
|
||||||
|
s8-to-i32
|
||||||
|
call-core $add_i32
|
||||||
|
i32-to-s8)
|
||||||
|
|
||||||
|
(@interface func (export "add_i16") (param s16) (param s16) (result s16)
|
||||||
|
arg.get 0
|
||||||
|
s16-to-i32
|
||||||
|
arg.get 1
|
||||||
|
s16-to-i32
|
||||||
|
call-core $add_i32
|
||||||
|
i32-to-s16)
|
||||||
|
|
||||||
|
(@interface func (export "add_i32") (param s32) (param s32) (result s32)
|
||||||
|
arg.get 0
|
||||||
|
s32-to-i32
|
||||||
|
arg.get 1
|
||||||
|
s32-to-i32
|
||||||
|
call-core $add_i32
|
||||||
|
i32-to-s32)
|
||||||
|
|
||||||
|
(@interface func (export "add_u8") (param s8) (param s8) (result s8)
|
||||||
|
arg.get 0
|
||||||
|
s8-to-i32
|
||||||
|
arg.get 1
|
||||||
|
s8-to-i32
|
||||||
|
call-core $add_i32
|
||||||
|
i32-to-s8)
|
||||||
|
|
||||||
|
(@interface func (export "add_u16") (param u16) (param u16) (result u16)
|
||||||
|
arg.get 0
|
||||||
|
u16-to-i32
|
||||||
|
arg.get 1
|
||||||
|
u16-to-i32
|
||||||
|
call-core $add_i32
|
||||||
|
i32-to-u16)
|
||||||
|
|
||||||
|
(@interface func (export "add_u32") (param u32) (param u32) (result u32)
|
||||||
|
arg.get 0
|
||||||
|
u32-to-i32
|
||||||
|
arg.get 1
|
||||||
|
u32-to-i32
|
||||||
|
call-core $add_i32
|
||||||
|
i32-to-u32)
|
||||||
|
)
|
5
crates/cli/tests/interface-types/memory-to-string.js
Normal file
5
crates/cli/tests/interface-types/memory-to-string.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const wasm = require('wasm');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
assert.strictEqual(wasm.foo(), 'foo');
|
||||||
|
assert.strictEqual(wasm.hexa(), 'hexa');
|
21
crates/cli/tests/interface-types/memory-to-string.wit
Normal file
21
crates/cli/tests/interface-types/memory-to-string.wit
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
|
||||||
|
(func $foo (result i32 i32)
|
||||||
|
i32.const 0
|
||||||
|
i32.const 3)
|
||||||
|
(func $hexa (result i32 i32)
|
||||||
|
i32.const 10
|
||||||
|
i32.const 4)
|
||||||
|
|
||||||
|
(data (i32.const 0) "foo")
|
||||||
|
(data (i32.const 10) "hexa")
|
||||||
|
|
||||||
|
(@interface func (export "foo") (result string)
|
||||||
|
call-core $foo
|
||||||
|
memory-to-string)
|
||||||
|
|
||||||
|
(@interface func (export "hexa") (result string)
|
||||||
|
call-core $hexa
|
||||||
|
memory-to-string)
|
||||||
|
)
|
5
crates/cli/tests/interface-types/no-wasm.js
Normal file
5
crates/cli/tests/interface-types/no-wasm.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
const wasm = require('wasm');
|
||||||
|
|
||||||
|
wasm.nop();
|
||||||
|
assert.strictEqual(wasm.roundtrip(1), 1);
|
5
crates/cli/tests/interface-types/no-wasm.wit
Normal file
5
crates/cli/tests/interface-types/no-wasm.wit
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
(module
|
||||||
|
(@interface func (export "nop"))
|
||||||
|
(@interface func (export "roundtrip") (param s32) (result s32)
|
||||||
|
arg.get 0)
|
||||||
|
)
|
11
crates/cli/tests/interface-types/string-to-memory.js
Normal file
11
crates/cli/tests/interface-types/string-to-memory.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const wasm = require('wasm');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const test = s => {
|
||||||
|
wasm.set(s);
|
||||||
|
assert.strictEqual(s, wasm.get());
|
||||||
|
};
|
||||||
|
|
||||||
|
test('');
|
||||||
|
test('x');
|
||||||
|
test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
27
crates/cli/tests/interface-types/string-to-memory.wit
Normal file
27
crates/cli/tests/interface-types/string-to-memory.wit
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
|
||||||
|
(global $glen (mut i32) (i32.const 0))
|
||||||
|
(global $gptr (mut i32) (i32.const 0))
|
||||||
|
|
||||||
|
(func $malloc (param i32) (result i32) i32.const 23)
|
||||||
|
|
||||||
|
(func $set (param $ptr i32) (param $len i32)
|
||||||
|
local.get $ptr
|
||||||
|
global.set $gptr
|
||||||
|
local.get $len
|
||||||
|
global.set $glen)
|
||||||
|
|
||||||
|
(func $get (result i32 i32)
|
||||||
|
global.get $gptr
|
||||||
|
global.get $glen)
|
||||||
|
|
||||||
|
(@interface func (export "set") (param string)
|
||||||
|
arg.get 0
|
||||||
|
string-to-memory $malloc
|
||||||
|
call-core $set)
|
||||||
|
|
||||||
|
(@interface func (export "get") (result string)
|
||||||
|
call-core $get
|
||||||
|
memory-to-string)
|
||||||
|
)
|
@ -105,7 +105,8 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let memory = wasm_conventions::get_memory(module)?;
|
let memory = wasm_conventions::get_memory(module)?;
|
||||||
let stack_pointer = wasm_conventions::get_shadow_stack_pointer(module)?;
|
let stack_pointer = wasm_conventions::get_shadow_stack_pointer(module)
|
||||||
|
.ok_or_else(|| anyhow!("failed to find shadow stack pointer"))?;
|
||||||
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 {
|
||||||
|
@ -33,7 +33,7 @@ pub fn get_memory(module: &Module) -> Result<MemoryId, Error> {
|
|||||||
///
|
///
|
||||||
/// It must have been previously added to the module's exports via
|
/// It must have been previously added to the module's exports via
|
||||||
/// `export_shadow_stack_pointer`.
|
/// `export_shadow_stack_pointer`.
|
||||||
pub fn get_shadow_stack_pointer(module: &Module) -> Result<GlobalId, Error> {
|
pub fn get_shadow_stack_pointer(module: &Module) -> Option<GlobalId> {
|
||||||
let candidates = module
|
let candidates = module
|
||||||
.globals
|
.globals
|
||||||
.iter()
|
.iter()
|
||||||
@ -48,12 +48,10 @@ pub fn get_shadow_stack_pointer(module: &Module) -> Result<GlobalId, Error> {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let ssp = match candidates.len() {
|
match candidates.len() {
|
||||||
0 => bail!("could not find the shadow stack pointer for the module"),
|
0 => None,
|
||||||
// TODO: have an actual check here.
|
// TODO: have an actual check here.
|
||||||
1 => candidates[0].id(),
|
1 => Some(candidates[0].id()),
|
||||||
_ => bail!("too many mutable globals to infer which is the shadow stack pointer"),
|
_ => None,
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(ssp)
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user