mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-04-25 14:12:13 +00:00
All numbers in WebAssembly are signed and then each operation on them may optionally have an unsigned version. This means that when we pass large signed numbers to JS they actually show up as large negative numbers even though JS numbers can faithfully represent the type. This is fixed by adding `>>>0` in a few locations in the generated bindings to coerce the JS value into an unsigned value. Closes #1388
375 lines
9.6 KiB
Rust
375 lines
9.6 KiB
Rust
use std::char;
|
|
|
|
macro_rules! tys {
|
|
($($a:ident)*) => (tys! { @ ($($a)*) 0 });
|
|
(@ () $v:expr) => {};
|
|
(@ ($a:ident $($b:ident)*) $v:expr) => {
|
|
const $a: u32 = $v;
|
|
tys!(@ ($($b)*) $v+1);
|
|
}
|
|
}
|
|
|
|
// NB: this list must be kept in sync with `src/describe.rs`
|
|
tys! {
|
|
I8
|
|
U8
|
|
I16
|
|
U16
|
|
I32
|
|
U32
|
|
I64
|
|
U64
|
|
F32
|
|
F64
|
|
BOOLEAN
|
|
FUNCTION
|
|
CLOSURE
|
|
STRING
|
|
REF
|
|
REFMUT
|
|
SLICE
|
|
VECTOR
|
|
ANYREF
|
|
ENUM
|
|
RUST_STRUCT
|
|
CHAR
|
|
OPTIONAL
|
|
UNIT
|
|
CLAMPED
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Descriptor {
|
|
I8,
|
|
U8,
|
|
I16,
|
|
U16,
|
|
I32,
|
|
U32,
|
|
I64,
|
|
U64,
|
|
F32,
|
|
F64,
|
|
Boolean,
|
|
Function(Box<Function>),
|
|
Closure(Box<Closure>),
|
|
Ref(Box<Descriptor>),
|
|
RefMut(Box<Descriptor>),
|
|
Slice(Box<Descriptor>),
|
|
Vector(Box<Descriptor>),
|
|
String,
|
|
Anyref,
|
|
Enum { hole: u32 },
|
|
RustStruct(String),
|
|
Char,
|
|
Option(Box<Descriptor>),
|
|
Unit,
|
|
Clamped(Box<Descriptor>),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Function {
|
|
pub arguments: Vec<Descriptor>,
|
|
pub shim_idx: u32,
|
|
pub ret: Descriptor,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Closure {
|
|
pub shim_idx: u32,
|
|
pub dtor_idx: u32,
|
|
pub function: Function,
|
|
pub mutable: bool,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub enum VectorKind {
|
|
I8,
|
|
U8,
|
|
ClampedU8,
|
|
I16,
|
|
U16,
|
|
I32,
|
|
U32,
|
|
I64,
|
|
U64,
|
|
F32,
|
|
F64,
|
|
String,
|
|
Anyref,
|
|
}
|
|
|
|
pub struct Number {
|
|
u32: bool,
|
|
}
|
|
|
|
impl Descriptor {
|
|
pub fn decode(mut data: &[u32]) -> Descriptor {
|
|
let descriptor = Descriptor::_decode(&mut data);
|
|
assert!(data.is_empty(), "remaining data {:?}", data);
|
|
descriptor
|
|
}
|
|
|
|
fn _decode(data: &mut &[u32]) -> Descriptor {
|
|
match get(data) {
|
|
I8 => Descriptor::I8,
|
|
I16 => Descriptor::I16,
|
|
I32 => Descriptor::I32,
|
|
I64 => Descriptor::I64,
|
|
U8 => Descriptor::U8,
|
|
U16 => Descriptor::U16,
|
|
U32 => Descriptor::U32,
|
|
U64 => Descriptor::U64,
|
|
F32 => Descriptor::F32,
|
|
F64 => Descriptor::F64,
|
|
BOOLEAN => Descriptor::Boolean,
|
|
FUNCTION => Descriptor::Function(Box::new(Function::decode(data))),
|
|
CLOSURE => Descriptor::Closure(Box::new(Closure::decode(data))),
|
|
REF => Descriptor::Ref(Box::new(Descriptor::_decode(data))),
|
|
REFMUT => Descriptor::RefMut(Box::new(Descriptor::_decode(data))),
|
|
SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data))),
|
|
VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data))),
|
|
OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data))),
|
|
STRING => Descriptor::String,
|
|
ANYREF => Descriptor::Anyref,
|
|
ENUM => Descriptor::Enum { hole: get(data) },
|
|
RUST_STRUCT => {
|
|
let name = (0..get(data))
|
|
.map(|_| char::from_u32(get(data)).unwrap())
|
|
.collect();
|
|
Descriptor::RustStruct(name)
|
|
}
|
|
CHAR => Descriptor::Char,
|
|
UNIT => Descriptor::Unit,
|
|
CLAMPED => Descriptor::Clamped(Box::new(Descriptor::_decode(data))),
|
|
other => panic!("unknown descriptor: {}", other),
|
|
}
|
|
}
|
|
|
|
pub fn unwrap_function(&self) -> &Function {
|
|
match *self {
|
|
Descriptor::Function(ref f) => f,
|
|
_ => panic!("not a function"),
|
|
}
|
|
}
|
|
|
|
/// Returns `Some` if this type is a number, and the returned `Number` type
|
|
/// can be accessed to learn more about what kind of number this is.
|
|
pub fn number(&self) -> Option<Number> {
|
|
match *self {
|
|
Descriptor::I8
|
|
| Descriptor::U8
|
|
| Descriptor::I16
|
|
| Descriptor::U16
|
|
| Descriptor::I32
|
|
| Descriptor::F32
|
|
| Descriptor::F64
|
|
| Descriptor::Enum { .. } => Some(Number { u32: false }),
|
|
Descriptor::U32 => Some(Number { u32: true }),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn is_wasm_native(&self) -> bool {
|
|
match *self {
|
|
Descriptor::I32 | Descriptor::U32 | Descriptor::F32 | Descriptor::F64 => true,
|
|
_ => return false,
|
|
}
|
|
}
|
|
|
|
pub fn is_abi_as_u32(&self) -> bool {
|
|
match *self {
|
|
Descriptor::I8 | Descriptor::U8 | Descriptor::I16 | Descriptor::U16 => true,
|
|
_ => return false,
|
|
}
|
|
}
|
|
|
|
pub fn get_64(&self) -> Option<bool> {
|
|
match *self {
|
|
Descriptor::I64 => Some(true),
|
|
Descriptor::U64 => Some(false),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn is_ref_anyref(&self) -> bool {
|
|
match *self {
|
|
Descriptor::Ref(ref s) => s.is_anyref(),
|
|
_ => return false,
|
|
}
|
|
}
|
|
|
|
pub fn closure(&self) -> Option<&Closure> {
|
|
match *self {
|
|
Descriptor::Closure(ref s) => Some(s),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn is_anyref(&self) -> bool {
|
|
match *self {
|
|
Descriptor::Anyref => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn vector_kind(&self) -> Option<VectorKind> {
|
|
let inner = match *self {
|
|
Descriptor::String => return Some(VectorKind::String),
|
|
Descriptor::Vector(ref d) => &**d,
|
|
Descriptor::Ref(ref d) => match **d {
|
|
Descriptor::Slice(ref d) => &**d,
|
|
Descriptor::String => return Some(VectorKind::String),
|
|
_ => return None,
|
|
},
|
|
Descriptor::RefMut(ref d) => match **d {
|
|
Descriptor::Slice(ref d) => &**d,
|
|
_ => return None,
|
|
},
|
|
Descriptor::Clamped(ref d) => match d.vector_kind()? {
|
|
VectorKind::U8 => return Some(VectorKind::ClampedU8),
|
|
_ => return None,
|
|
},
|
|
_ => return None,
|
|
};
|
|
match *inner {
|
|
Descriptor::I8 => Some(VectorKind::I8),
|
|
Descriptor::I16 => Some(VectorKind::I16),
|
|
Descriptor::I32 => Some(VectorKind::I32),
|
|
Descriptor::I64 => Some(VectorKind::I64),
|
|
Descriptor::U8 => Some(VectorKind::U8),
|
|
Descriptor::U16 => Some(VectorKind::U16),
|
|
Descriptor::U32 => Some(VectorKind::U32),
|
|
Descriptor::U64 => Some(VectorKind::U64),
|
|
Descriptor::F32 => Some(VectorKind::F32),
|
|
Descriptor::F64 => Some(VectorKind::F64),
|
|
Descriptor::Anyref => Some(VectorKind::Anyref),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn rust_struct(&self) -> Option<&str> {
|
|
let inner = match *self {
|
|
Descriptor::Ref(ref d) => &**d,
|
|
Descriptor::RefMut(ref d) => &**d,
|
|
ref d => d,
|
|
};
|
|
match *inner {
|
|
Descriptor::RustStruct(ref s) => Some(s),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn stack_closure(&self) -> Option<(&Function, bool)> {
|
|
let (inner, mutable) = match *self {
|
|
Descriptor::Ref(ref d) => (&**d, false),
|
|
Descriptor::RefMut(ref d) => (&**d, true),
|
|
_ => return None,
|
|
};
|
|
match *inner {
|
|
Descriptor::Function(ref f) => Some((f, mutable)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn is_by_ref(&self) -> bool {
|
|
match *self {
|
|
Descriptor::Ref(_) | Descriptor::RefMut(_) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_clamped_by_ref(&self) -> bool {
|
|
match self {
|
|
Descriptor::Clamped(d) => d.is_by_ref(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_mut_ref(&self) -> bool {
|
|
match *self {
|
|
Descriptor::RefMut(_) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get(a: &mut &[u32]) -> u32 {
|
|
let ret = a[0];
|
|
*a = &a[1..];
|
|
ret
|
|
}
|
|
|
|
impl Closure {
|
|
fn decode(data: &mut &[u32]) -> Closure {
|
|
let shim_idx = get(data);
|
|
let dtor_idx = get(data);
|
|
let mutable = get(data) == REFMUT;
|
|
assert_eq!(get(data), FUNCTION);
|
|
Closure {
|
|
shim_idx,
|
|
dtor_idx,
|
|
mutable,
|
|
function: Function::decode(data),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Function {
|
|
fn decode(data: &mut &[u32]) -> Function {
|
|
let shim_idx = get(data);
|
|
let arguments = (0..get(data))
|
|
.map(|_| Descriptor::_decode(data))
|
|
.collect::<Vec<_>>();
|
|
Function {
|
|
arguments,
|
|
shim_idx,
|
|
ret: Descriptor::_decode(data),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl VectorKind {
|
|
pub fn js_ty(&self) -> &str {
|
|
match *self {
|
|
VectorKind::String => "string",
|
|
VectorKind::I8 => "Int8Array",
|
|
VectorKind::U8 => "Uint8Array",
|
|
VectorKind::ClampedU8 => "Uint8ClampedArray",
|
|
VectorKind::I16 => "Int16Array",
|
|
VectorKind::U16 => "Uint16Array",
|
|
VectorKind::I32 => "Int32Array",
|
|
VectorKind::U32 => "Uint32Array",
|
|
VectorKind::I64 => "BigInt64Array",
|
|
VectorKind::U64 => "BigUint64Array",
|
|
VectorKind::F32 => "Float32Array",
|
|
VectorKind::F64 => "Float64Array",
|
|
VectorKind::Anyref => "any[]",
|
|
}
|
|
}
|
|
|
|
pub fn size(&self) -> usize {
|
|
match *self {
|
|
VectorKind::String => 1,
|
|
VectorKind::I8 => 1,
|
|
VectorKind::U8 => 1,
|
|
VectorKind::ClampedU8 => 1,
|
|
VectorKind::I16 => 2,
|
|
VectorKind::U16 => 2,
|
|
VectorKind::I32 => 4,
|
|
VectorKind::U32 => 4,
|
|
VectorKind::I64 => 8,
|
|
VectorKind::U64 => 8,
|
|
VectorKind::F32 => 4,
|
|
VectorKind::F64 => 8,
|
|
VectorKind::Anyref => 4,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Number {
|
|
pub fn is_u32(&self) -> bool {
|
|
self.u32
|
|
}
|
|
}
|