From bf109de9b2a46de0d8e7ffea198823e5a6744a1b Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Fri, 19 Aug 2022 03:23:39 +1000 Subject: [PATCH] Pass 64-bit integers directly across the wasm ABI boundary (#3037) Previously, they were passed as a pair of `i32`s; this changes them to be passed as a single `i64`, which is directly converted to a bigint on the JS end. I think they were previously passed as a pair because `wasm-bindgen`'s support for 64-bit integers predated the WebAssembly bigint integration that translates `i64`s to bigints. However, that feature has been around for quite a while now, so I think it should be fine to use. I also bumped the schema version - although the schema itself hasn't changed, the ABI has, so using an older version of the CLI with this version of the `wasm-bindgen` crate or vice versa won't work and I want the version-mismatch warning to show up. Fixes cloudflare/serde-wasm-bindgen#28. This doesn't rely on a global `BigInt64Array` like the previous `i32`-pair version, which means that code using 64-bit integers will no longer fail at load time in browsers without bigint support. --- crates/cli-support/src/js/binding.rs | 158 ++++++++-------------- crates/cli-support/src/js/mod.rs | 48 ++----- crates/cli-support/src/wit/incoming.rs | 28 +--- crates/cli-support/src/wit/mod.rs | 2 +- crates/cli-support/src/wit/outgoing.rs | 40 ++---- crates/cli-support/src/wit/section.rs | 9 +- crates/cli-support/src/wit/standard.rs | 17 --- crates/shared/src/lib.rs | 2 +- crates/shared/src/schema_hash_approval.rs | 2 +- src/convert/impls.rs | 89 ++---------- src/convert/traits.rs | 2 + 11 files changed, 105 insertions(+), 292 deletions(-) diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index c29f2da5433..008e529f213 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -8,7 +8,7 @@ use crate::js::Context; use crate::wit::InstructionData; use crate::wit::{Adapter, AdapterId, AdapterKind, AdapterType, Instruction}; use anyhow::{anyhow, bail, Error}; -use walrus::Module; +use walrus::{Module, ValType}; /// A one-size-fits-all builder for processing WebIDL bindings and generating /// JS. @@ -437,6 +437,14 @@ impl<'a, 'b> JsBuilder<'a, 'b> { self.prelude(&format!("_assertNum({});", arg)); } + fn assert_bigint(&mut self, arg: &str) { + if !self.cx.config.debug { + return; + } + self.cx.expose_assert_bigint(); + self.prelude(&format!("_assertBigInt({});", arg)); + } + fn assert_bool(&mut self, arg: &str) { if !self.cx.config.debug { return; @@ -455,6 +463,16 @@ impl<'a, 'b> JsBuilder<'a, 'b> { self.prelude("}"); } + fn assert_optional_bigint(&mut self, arg: &str) { + if !self.cx.config.debug { + return; + } + self.cx.expose_is_like_none(); + self.prelude(&format!("if (!isLikeNone({})) {{", arg)); + self.assert_bigint(arg); + self.prelude("}"); + } + fn assert_optional_bool(&mut self, arg: &str) { if !self.cx.config.debug { return; @@ -560,9 +578,18 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } } - Instruction::Standard(wit_walrus::Instruction::IntToWasm { trap: false, .. }) => { + Instruction::Standard(wit_walrus::Instruction::IntToWasm { + trap: false, input, .. + }) => { let val = js.pop(); - js.assert_number(&val); + if matches!( + input, + wit_walrus::ValType::I64 | wit_walrus::ValType::S64 | wit_walrus::ValType::U64 + ) { + js.assert_bigint(&val); + } else { + js.assert_number(&val); + } js.push(val); } @@ -577,6 +604,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> let val = js.pop(); match output { wit_walrus::ValType::U32 => js.push(format!("{} >>> 0", val)), + wit_walrus::ValType::U64 => js.push(format!("BigInt.asUintN(64, {val})")), _ => js.push(val), } } @@ -618,6 +646,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> Instruction::StoreRetptr { ty, offset, mem } => { let (mem, size) = match ty { AdapterType::I32 => (js.cx.expose_int32_memory(*mem), 4), + AdapterType::I64 => (js.cx.expose_int64_memory(*mem), 8), AdapterType::F32 => (js.cx.expose_f32_memory(*mem), 4), AdapterType::F64 => (js.cx.expose_f64_memory(*mem), 8), other => bail!("invalid aggregate return type {:?}", other), @@ -639,6 +668,7 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> Instruction::LoadRetptr { ty, offset, mem } => { let (mem, quads) = match ty { AdapterType::I32 => (js.cx.expose_int32_memory(*mem), 1), + AdapterType::I64 => (js.cx.expose_int64_memory(*mem), 2), AdapterType::F32 => (js.cx.expose_f32_memory(*mem), 1), AdapterType::F64 => (js.cx.expose_f64_memory(*mem), 2), other => bail!("invalid aggregate return type {:?}", other), @@ -712,52 +742,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> js.push(format!("ptr{}", i)); } - Instruction::I32Split64 { signed } => { - let val = js.pop(); - let f = if *signed { - js.cx.expose_int64_cvt_shim() - } else { - js.cx.expose_uint64_cvt_shim() - }; - let i = js.tmp(); - js.prelude(&format!( - " - {f}[0] = {val}; - const low{i} = u32CvtShim[0]; - const high{i} = u32CvtShim[1]; - ", - i = i, - f = f, - val = val, - )); - js.push(format!("low{}", i)); - js.push(format!("high{}", i)); - } - - Instruction::I32SplitOption64 { signed } => { - let val = js.pop(); - js.cx.expose_is_like_none(); - let f = if *signed { - js.cx.expose_int64_cvt_shim() - } else { - js.cx.expose_uint64_cvt_shim() - }; - let i = js.tmp(); - js.prelude(&format!( - "\ - {f}[0] = isLikeNone({val}) ? BigInt(0) : {val}; - const low{i} = u32CvtShim[0]; - const high{i} = u32CvtShim[1]; - ", - i = i, - f = f, - val = val, - )); - js.push(format!("!isLikeNone({0})", val)); - js.push(format!("low{}", i)); - js.push(format!("high{}", i)); - } - Instruction::I32FromOptionExternref { table_and_alloc } => { let val = js.pop(); js.cx.expose_is_like_none(); @@ -803,12 +787,19 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> js.push(format!("isLikeNone({0}) ? {1} : {0}", val, hole)); } - Instruction::FromOptionNative { .. } => { + Instruction::FromOptionNative { ty } => { let val = js.pop(); js.cx.expose_is_like_none(); - js.assert_optional_number(&val); + if *ty == ValType::I64 { + js.assert_optional_bigint(&val); + } else { + js.assert_optional_number(&val); + } js.push(format!("!isLikeNone({0})", val)); - js.push(format!("isLikeNone({0}) ? 0 : {0}", val)); + js.push(format!( + "isLikeNone({val}) ? 0{n} : {val}", + n = if *ty == ValType::I64 { "n" } else { "" } + )); } Instruction::VectorToMemory { kind, malloc, mem } => { @@ -1001,29 +992,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> js.push(format!("String.fromCodePoint({})", val)); } - Instruction::I64FromLoHi { signed } => { - let f = if *signed { - js.cx.expose_int64_cvt_shim() - } else { - js.cx.expose_uint64_cvt_shim() - }; - let i = js.tmp(); - let high = js.pop(); - let low = js.pop(); - js.prelude(&format!( - "\ - u32CvtShim[0] = {low}; - u32CvtShim[1] = {high}; - const n{i} = {f}[0]; - ", - low = low, - high = high, - f = f, - i = i, - )); - js.push(format!("n{}", i)) - } - Instruction::RustFromI32 { class } => { js.cx.require_class_wrap(class); let val = js.pop(); @@ -1183,14 +1151,21 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> js.push(format!("{0} === 0xFFFFFF ? undefined : {0}", val)); } - Instruction::ToOptionNative { ty: _, signed } => { + Instruction::ToOptionNative { ty, signed } => { let val = js.pop(); let present = js.pop(); js.push(format!( - "{} === 0 ? undefined : {}{}", + "{} === 0 ? undefined : {}", present, - val, - if *signed { "" } else { " >>> 0" }, + if *signed { + val + } else { + match ty { + ValType::I32 => format!("{val} >>> 0"), + ValType::I64 => format!("BigInt.asUintN(64, {val})"), + _ => unreachable!("unsigned non-integer"), + } + }, )); } @@ -1211,31 +1186,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> let val = js.pop(); js.push(format!("{0} === {1} ? undefined : {0}", val, hole)); } - - Instruction::Option64FromI32 { signed } => { - let f = if *signed { - js.cx.expose_int64_cvt_shim() - } else { - js.cx.expose_uint64_cvt_shim() - }; - let i = js.tmp(); - let high = js.pop(); - let low = js.pop(); - let present = js.pop(); - js.prelude(&format!( - " - u32CvtShim[0] = {low}; - u32CvtShim[1] = {high}; - const n{i} = {present} === 0 ? undefined : {f}[0]; - ", - present = present, - low = low, - high = high, - f = f, - i = i, - )); - js.push(format!("n{}", i)); - } } Ok(()) } diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 0e8934ed809..063100f43ae 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1126,6 +1126,19 @@ impl<'a> Context<'a> { )); } + fn expose_assert_bigint(&mut self) { + if !self.should_write_global("assert_bigint") { + return; + } + self.global(&format!( + " + function _assertBigInt(n) {{ + if (typeof(n) !== 'bigint') throw new Error('expected a bigint argument'); + }} + " + )); + } + fn expose_assert_bool(&mut self) { if !self.should_write_global("assert_bool") { return; @@ -2036,41 +2049,6 @@ impl<'a> Context<'a> { ); } - fn expose_u32_cvt_shim(&mut self) -> &'static str { - let name = "u32CvtShim"; - if !self.should_write_global(name) { - return name; - } - self.global(&format!("const {} = new Uint32Array(2);", name)); - name - } - - fn expose_int64_cvt_shim(&mut self) -> &'static str { - let name = "int64CvtShim"; - if !self.should_write_global(name) { - return name; - } - let n = self.expose_u32_cvt_shim(); - self.global(&format!( - "const {} = new BigInt64Array({}.buffer);", - name, n - )); - name - } - - fn expose_uint64_cvt_shim(&mut self) -> &'static str { - let name = "uint64CvtShim"; - if !self.should_write_global(name) { - return name; - } - let n = self.expose_u32_cvt_shim(); - self.global(&format!( - "const {} = new BigUint64Array({}.buffer);", - name, n - )); - name - } - fn expose_is_like_none(&mut self) { if !self.should_write_global("is_like_none") { return; diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 1778516caad..1c8e2cb169c 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -89,8 +89,8 @@ impl InstructionBuilder<'_, '_> { Descriptor::U16 => self.number(WitVT::U16, WasmVT::I32), Descriptor::I32 => self.number(WitVT::S32, WasmVT::I32), Descriptor::U32 => self.number(WitVT::U32, WasmVT::I32), - Descriptor::I64 => self.number64(true), - Descriptor::U64 => self.number64(false), + Descriptor::I64 => self.number(WitVT::S64, WasmVT::I64), + Descriptor::U64 => self.number(WitVT::U64, WasmVT::I64), Descriptor::F32 => { self.get(AdapterType::F32); self.output.push(AdapterType::F32); @@ -256,17 +256,7 @@ impl InstructionBuilder<'_, '_> { Descriptor::U32 => self.in_option_native(ValType::I32), Descriptor::F32 => self.in_option_native(ValType::F32), Descriptor::F64 => self.in_option_native(ValType::F64), - Descriptor::I64 | Descriptor::U64 => { - let (signed, ty) = match arg { - Descriptor::I64 => (true, AdapterType::S64.option()), - _ => (false, AdapterType::U64.option()), - }; - self.instruction( - &[ty], - Instruction::I32SplitOption64 { signed }, - &[AdapterType::I32, AdapterType::I32, AdapterType::I32], - ); - } + Descriptor::I64 | Descriptor::U64 => self.in_option_native(ValType::I64), Descriptor::Boolean => { self.instruction( &[AdapterType::Bool.option()], @@ -396,18 +386,6 @@ impl InstructionBuilder<'_, '_> { ); } - fn number64(&mut self, signed: bool) { - self.instruction( - &[if signed { - AdapterType::S64 - } else { - AdapterType::U64 - }], - Instruction::I32Split64 { signed }, - &[AdapterType::I32, AdapterType::I32], - ); - } - fn in_option_native(&mut self, wasm: ValType) { let ty = AdapterType::from_wasm(wasm).unwrap(); self.instruction( diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index c8da887542f..4e3a485f918 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -1646,7 +1646,7 @@ impl StructUnpacker { fn read_ty(&mut self, ty: &AdapterType) -> Result { let (quads, alignment) = match ty { AdapterType::I32 | AdapterType::U32 | AdapterType::F32 => (1, 1), - AdapterType::F64 => (2, 2), + AdapterType::I64 | AdapterType::U64 | AdapterType::F64 => (2, 2), other => bail!("invalid aggregate return type {:?}", other), }; Ok(self.append(quads, alignment)) diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index e2598f7e920..1ea9c17bf93 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -63,6 +63,8 @@ impl InstructionBuilder<'_, '_> { Descriptor::U16 => self.outgoing_i32(AdapterType::U16), Descriptor::I32 => self.outgoing_i32(AdapterType::S32), Descriptor::U32 => self.outgoing_i32(AdapterType::U32), + Descriptor::I64 => self.outgoing_i64(AdapterType::I64), + Descriptor::U64 => self.outgoing_i64(AdapterType::U64), Descriptor::F32 => { self.get(AdapterType::F32); self.output.push(AdapterType::F32); @@ -81,22 +83,6 @@ impl InstructionBuilder<'_, '_> { ); } - Descriptor::I64 | Descriptor::U64 => { - let signed = match arg { - Descriptor::I64 => true, - _ => false, - }; - self.instruction( - &[AdapterType::I32, AdapterType::I32], - Instruction::I64FromLoHi { signed }, - &[if signed { - AdapterType::S64 - } else { - AdapterType::U64 - }], - ); - } - Descriptor::RustStruct(class) => { self.instruction( &[AdapterType::I32], @@ -277,19 +263,10 @@ impl InstructionBuilder<'_, '_> { Descriptor::U16 => self.out_option_sentinel(AdapterType::U16), Descriptor::I32 => self.option_native(true, ValType::I32), Descriptor::U32 => self.option_native(false, ValType::I32), + Descriptor::I64 => self.option_native(true, ValType::I64), + Descriptor::U64 => self.option_native(false, ValType::I64), Descriptor::F32 => self.option_native(true, ValType::F32), Descriptor::F64 => self.option_native(true, ValType::F64), - Descriptor::I64 | Descriptor::U64 => { - let (signed, ty) = match arg { - Descriptor::I64 => (true, AdapterType::S64.option()), - _ => (false, AdapterType::U64.option()), - }; - self.instruction( - &[AdapterType::I32, AdapterType::I32, AdapterType::I32], - Instruction::Option64FromI32 { signed }, - &[ty], - ); - } Descriptor::Boolean => { self.instruction( &[AdapterType::I32], @@ -541,6 +518,15 @@ impl InstructionBuilder<'_, '_> { self.instruction(&[AdapterType::I32], Instruction::Standard(std), &[output]); } + fn outgoing_i64(&mut self, output: AdapterType) { + let std = wit_walrus::Instruction::WasmToInt { + input: walrus::ValType::I64, + output: output.to_wit().unwrap(), + trap: false, + }; + self.instruction(&[AdapterType::I64], Instruction::Standard(std), &[output]); + } + fn cached_string(&mut self, optional: bool, owned: bool) -> Result<(), Error> { let mem = self.cx.memory()?; let free = self.cx.free()?; diff --git a/crates/cli-support/src/wit/section.rs b/crates/cli-support/src/wit/section.rs index 1fe532d77b1..112256193cb 100644 --- a/crates/cli-support/src/wit/section.rs +++ b/crates/cli-support/src/wit/section.rs @@ -256,11 +256,7 @@ fn translate_instruction( | RustFromI32 { .. } => { bail!("rust types aren't supported in wasm interface types"); } - I32Split64 { .. } | I64FromLoHi { .. } => { - bail!("64-bit integers aren't supported in wasm-bindgen"); - } - I32SplitOption64 { .. } - | I32FromOptionExternref { .. } + I32FromOptionExternref { .. } | I32FromOptionU32Sentinel | I32FromOptionRust { .. } | I32FromOptionBool @@ -276,8 +272,7 @@ fn translate_instruction( | ToOptionNative { .. } | OptionBoolFromI32 | OptionCharFromI32 - | OptionEnumFromI32 { .. } - | Option64FromI32 { .. } => { + | OptionEnumFromI32 { .. } => { bail!("optional types aren't supported in wasm bindgen"); } UnwrapResult { .. } | UnwrapResultString { .. } => { diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index 78df620d765..d07c593491a 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -146,16 +146,6 @@ pub enum Instruction { I32FromOptionRust { class: String, }, - /// Pops an `s64` or `u64` from the stack, pushing two `i32` values. - I32Split64 { - signed: bool, - }, - /// Pops an `s64` or `u64` from the stack, pushing three `i32` values. - /// First is the "some/none" bit, and the next is the low bits, and the - /// next is the high bits. - I32SplitOption64 { - signed: bool, - }, /// Pops an `externref` from the stack, pushes either 0 if it's "none" or and /// index into the owned wasm table it was stored at if it's "some" I32FromOptionExternref { @@ -240,10 +230,6 @@ pub enum Instruction { }, /// pops `i32`, pushes string from that `char` StringFromChar, - /// pops two `i32`, pushes a 64-bit number - I64FromLoHi { - signed: bool, - }, /// pops `i32`, pushes an externref for the wrapped rust class RustFromI32 { class: String, @@ -302,9 +288,6 @@ pub enum Instruction { OptionEnumFromI32 { hole: u32, }, - Option64FromI32 { - signed: bool, - }, } impl AdapterType { diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index dfa338a3747..46ed0b8a72a 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -6,7 +6,7 @@ mod schema_hash_approval; // This gets changed whenever our schema changes. // At this time versions of wasm-bindgen and wasm-bindgen-cli are required to have the exact same // SCHEMA_VERSION in order to work together. -pub const SCHEMA_VERSION: &str = "0.2.82"; +pub const SCHEMA_VERSION: &str = "0.2.83"; #[macro_export] macro_rules! shared_api { diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index 8b26fc3c71c..426c9ed5b6f 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &'static str = "18229224525184165582"; +const APPROVED_SCHEMA_FILE_HASH: &'static str = "6377323169918973918"; #[test] fn schema_version() { diff --git a/src/convert/impls.rs b/src/convert/impls.rs index 429a32896af..0179cde2e4b 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -25,37 +25,36 @@ pub struct WasmOptionalU32 { unsafe impl WasmAbi for WasmOptionalU32 {} #[repr(C)] -pub struct WasmOptionalF32 { +pub struct WasmOptionalI64 { pub present: u32, - pub value: f32, + pub value: i64, } -unsafe impl WasmAbi for WasmOptionalF32 {} +unsafe impl WasmAbi for WasmOptionalI64 {} #[repr(C)] -pub struct WasmOptionalF64 { +pub struct WasmOptionalU64 { pub present: u32, - pub value: f64, + pub value: u64, } -unsafe impl WasmAbi for WasmOptionalF64 {} +unsafe impl WasmAbi for WasmOptionalU64 {} #[repr(C)] -pub struct Wasm64 { - pub low: u32, - pub high: u32, +pub struct WasmOptionalF32 { + pub present: u32, + pub value: f32, } -unsafe impl WasmAbi for Wasm64 {} +unsafe impl WasmAbi for WasmOptionalF32 {} #[repr(C)] -pub struct WasmOptional64 { +pub struct WasmOptionalF64 { pub present: u32, - pub low: u32, - pub high: u32, + pub value: f64, } -unsafe impl WasmAbi for WasmOptional64 {} +unsafe impl WasmAbi for WasmOptionalF64 {} macro_rules! type_wasm_native { ($($t:tt as $c:tt => $r:tt)*) => ($( @@ -111,6 +110,8 @@ type_wasm_native!( isize as i32 => WasmOptionalI32 u32 as u32 => WasmOptionalU32 usize as u32 => WasmOptionalU32 + i64 as i64 => WasmOptionalI64 + u64 as u64 => WasmOptionalU64 f32 as f32 => WasmOptionalF32 f64 as f64 => WasmOptionalF64 ); @@ -145,66 +146,6 @@ macro_rules! type_abi_as_u32 { type_abi_as_u32!(i8 u8 i16 u16); -macro_rules! type_64 { - ($($t:tt)*) => ($( - impl IntoWasmAbi for $t { - type Abi = Wasm64; - - #[inline] - fn into_abi(self) -> Wasm64 { - Wasm64 { - low: self as u32, - high: (self >> 32) as u32, - } - } - } - - impl FromWasmAbi for $t { - type Abi = Wasm64; - - #[inline] - unsafe fn from_abi(js: Wasm64) -> $t { - $t::from(js.low) | ($t::from(js.high) << 32) - } - } - - impl IntoWasmAbi for Option<$t> { - type Abi = WasmOptional64; - - #[inline] - fn into_abi(self) -> WasmOptional64 { - match self { - None => WasmOptional64 { - present: 0, - low: 0 as u32, - high: 0 as u32, - }, - Some(me) => WasmOptional64 { - present: 1, - low: me as u32, - high: (me >> 32) as u32, - }, - } - } - } - - impl FromWasmAbi for Option<$t> { - type Abi = WasmOptional64; - - #[inline] - unsafe fn from_abi(js: WasmOptional64) -> Self { - if js.present == 0 { - None - } else { - Some($t::from(js.low) | ($t::from(js.high) << 32)) - } - } - } - )*) -} - -type_64!(i64 u64); - impl IntoWasmAbi for bool { type Abi = u32; diff --git a/src/convert/traits.rs b/src/convert/traits.rs index dda66365012..b9d12b4c891 100644 --- a/src/convert/traits.rs +++ b/src/convert/traits.rs @@ -100,6 +100,8 @@ pub unsafe trait WasmAbi {} unsafe impl WasmAbi for u32 {} unsafe impl WasmAbi for i32 {} +unsafe impl WasmAbi for u64 {} +unsafe impl WasmAbi for i64 {} unsafe impl WasmAbi for f32 {} unsafe impl WasmAbi for f64 {}