mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 18:20:51 +00:00
Merge pull request #1065 from alexcrichton/describe-closures
Move closure shims into the descriptor
This commit is contained in:
commit
91e9495805
@ -501,6 +501,7 @@ impl TryToTokens for ast::Export {
|
||||
&export,
|
||||
quote! {
|
||||
inform(FUNCTION);
|
||||
inform(0);
|
||||
inform(#nargs);
|
||||
#(<#argtys as WasmDescribe>::describe();)*
|
||||
#describe_ret
|
||||
@ -999,6 +1000,7 @@ impl<'a> ToTokens for DescribeImport<'a> {
|
||||
&f.shim,
|
||||
quote! {
|
||||
inform(FUNCTION);
|
||||
inform(0);
|
||||
inform(#nargs);
|
||||
#(<#argtys as WasmDescribe>::describe();)*
|
||||
#inform_ret
|
||||
|
@ -70,11 +70,14 @@ pub enum 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,
|
||||
}
|
||||
@ -293,9 +296,13 @@ fn get(a: &mut &[u32]) -> u32 {
|
||||
|
||||
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),
|
||||
}
|
||||
@ -304,11 +311,13 @@ impl Closure {
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -47,11 +47,12 @@ pub fn rewrite(input: &mut Context) -> Result<(), Error> {
|
||||
// If this was an imported function we didn't reorder those, so nothing
|
||||
// to do.
|
||||
if idx < old_num_imports {
|
||||
return idx
|
||||
idx
|
||||
} else {
|
||||
// ... otherwise we're injecting a number of new imports, so offset
|
||||
// everything.
|
||||
idx + info.code_idx_to_descriptor.len() as u32
|
||||
}
|
||||
// ... otherwise we're injecting a number of new imports, so offset
|
||||
// everything.
|
||||
idx + info.code_idx_to_descriptor.len() as u32
|
||||
}).remap_module(input.module);
|
||||
|
||||
info.delete_function_table_entries(input);
|
||||
@ -252,9 +253,9 @@ impl ClosureDescriptors {
|
||||
input.expose_add_heap_object();
|
||||
input.function_table_needed = true;
|
||||
let body = format!(
|
||||
"function(a, b, fi, di, _ignored) {{
|
||||
const f = wasm.__wbg_function_table.get(fi);
|
||||
const d = wasm.__wbg_function_table.get(di);
|
||||
"function(a, b, _ignored) {{
|
||||
const f = wasm.__wbg_function_table.get({});
|
||||
const d = wasm.__wbg_function_table.get({});
|
||||
const cb = {};
|
||||
cb.a = a;
|
||||
cb.cnt = 1;
|
||||
@ -262,6 +263,8 @@ impl ClosureDescriptors {
|
||||
real.original = cb;
|
||||
return addHeapObject(real);
|
||||
}}",
|
||||
closure.shim_idx,
|
||||
closure.dtor_idx,
|
||||
js,
|
||||
);
|
||||
input.export(&import_name, &body, None);
|
||||
|
@ -256,7 +256,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
||||
self.cx.expose_uint64_cvt_shim()
|
||||
};
|
||||
self.cx.expose_uint32_memory();
|
||||
self.cx.expose_global_argument_ptr()?;
|
||||
self.js_arguments.push((name.clone(), "BigInt".to_string()));
|
||||
self.prelude(&format!(
|
||||
"
|
||||
@ -374,7 +373,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
|
||||
self.cx.expose_uint64_cvt_shim()
|
||||
};
|
||||
self.cx.expose_uint32_memory();
|
||||
self.cx.expose_global_argument_ptr()?;
|
||||
self.js_arguments.push((name.clone(), "BigInt".to_string()));
|
||||
self.prelude(&format!(
|
||||
"
|
||||
|
@ -459,8 +459,10 @@ impl<'a> Context<'a> {
|
||||
Ok(String::from("function(idx) { throw takeObject(idx); }"))
|
||||
})?;
|
||||
|
||||
closures::rewrite(self).with_context(|_| {
|
||||
"failed to generate internal closure shims"
|
||||
})?;
|
||||
self.unexport_unused_internal_exports();
|
||||
closures::rewrite(self)?;
|
||||
|
||||
// Handle the `start` function, if one was specified. If we're in a
|
||||
// --test mode (such as wasm-bindgen-test-runner) then we skip this
|
||||
@ -1682,23 +1684,6 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn expose_get_global_argument(&mut self) -> Result<(), Error> {
|
||||
if !self.exposed_globals.insert("get_global_argument") {
|
||||
return Ok(());
|
||||
}
|
||||
self.expose_uint32_memory();
|
||||
self.expose_global_argument_ptr()?;
|
||||
self.global(
|
||||
"
|
||||
function getGlobalArgument(arg) {
|
||||
const idx = globalArgumentPtr() / 4 + arg;
|
||||
return getUint32Memory()[idx];
|
||||
}
|
||||
",
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expose_global_argument_ptr(&mut self) -> Result<(), Error> {
|
||||
if !self.exposed_globals.insert("global_argument_ptr") {
|
||||
return Ok(());
|
||||
@ -2337,7 +2322,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
self.generate_enum(e);
|
||||
}
|
||||
for s in self.program.structs.iter() {
|
||||
self.generate_struct(s)?;
|
||||
self.generate_struct(s).with_context(|_| {
|
||||
format!(
|
||||
"failed to generate bindings for Rust struct `{}`",
|
||||
s.name,
|
||||
)
|
||||
})?;
|
||||
}
|
||||
for s in self.program.typescript_custom_sections.iter() {
|
||||
self.cx.typescript.push_str(s);
|
||||
|
@ -252,6 +252,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
}
|
||||
|
||||
if let Some((f, mutable)) = arg.stack_closure() {
|
||||
let arg2 = self.shim_argument();
|
||||
let (js, _ts, _js_doc) = {
|
||||
let mut builder = Js2Rust::new("", self.cx);
|
||||
if mutable {
|
||||
@ -268,20 +269,19 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
.process(f)?
|
||||
.finish("function", "this.f")
|
||||
};
|
||||
self.cx.expose_get_global_argument()?;
|
||||
self.cx.function_table_needed = true;
|
||||
let next_global = self.global_idx();
|
||||
self.global_idx();
|
||||
self.prelude(&format!(
|
||||
"\
|
||||
let cb{0} = {js};\n\
|
||||
cb{0}.f = wasm.__wbg_function_table.get({0});\n\
|
||||
cb{0}.a = getGlobalArgument({next_global});\n\
|
||||
cb{0}.b = getGlobalArgument({next_global} + 1);\n\
|
||||
cb{0}.f = wasm.__wbg_function_table.get({idx});\n\
|
||||
cb{0}.a = {0};\n\
|
||||
cb{0}.b = {1};\n\
|
||||
",
|
||||
abi,
|
||||
arg2,
|
||||
js = js,
|
||||
next_global = next_global
|
||||
idx = f.shim_idx,
|
||||
));
|
||||
self.finally(&format!("cb{0}.a = cb{0}.b = 0;", abi));
|
||||
self.js_arguments.push(format!("cb{0}.bind(cb{0})", abi));
|
||||
|
@ -353,8 +353,6 @@ impl Interpreter {
|
||||
self.descriptor_table_idx = Some(self.stack.pop().unwrap() as u32);
|
||||
self.stack.pop();
|
||||
self.stack.pop();
|
||||
self.stack.pop();
|
||||
self.stack.pop();
|
||||
self.stack.push(0);
|
||||
} else {
|
||||
self.call(*idx, sections);
|
||||
|
@ -197,20 +197,16 @@ impl<T> Closure<T>
|
||||
unsafe fn breaks_if_inlined<T: WasmClosure + ?Sized>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
invoke: u32,
|
||||
destroy: u32,
|
||||
) -> u32 {
|
||||
super::__wbindgen_describe_closure(
|
||||
a as u32,
|
||||
b as u32,
|
||||
invoke,
|
||||
destroy,
|
||||
describe::<T> as u32,
|
||||
)
|
||||
}
|
||||
|
||||
let idx = unsafe {
|
||||
breaks_if_inlined::<T>(a, b, T::invoke_fn(), T::destroy_fn())
|
||||
breaks_if_inlined::<T>(a, b)
|
||||
};
|
||||
|
||||
Closure {
|
||||
@ -294,9 +290,6 @@ impl<T> Drop for Closure<T>
|
||||
#[doc(hidden)]
|
||||
pub unsafe trait WasmClosure: 'static {
|
||||
fn describe();
|
||||
|
||||
fn invoke_fn() -> u32;
|
||||
fn destroy_fn() -> u32;
|
||||
}
|
||||
|
||||
// The memory safety here in these implementations below is a bit tricky. We
|
||||
@ -322,11 +315,6 @@ macro_rules! doit {
|
||||
R: ReturnWasmAbi + 'static,
|
||||
{
|
||||
fn describe() {
|
||||
<&Self>::describe();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn invoke_fn() -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
@ -350,12 +338,10 @@ macro_rules! doit {
|
||||
};
|
||||
ret.return_abi(&mut GlobalStack::new())
|
||||
}
|
||||
invoke::<$($var,)* R> as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn destroy_fn() -> u32 {
|
||||
unsafe extern "C" fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
inform(invoke::<$($var,)* R> as u32);
|
||||
|
||||
unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
) {
|
||||
@ -364,7 +350,9 @@ macro_rules! doit {
|
||||
fields: (a, b)
|
||||
}.ptr));
|
||||
}
|
||||
destroy::<$($var,)* R> as u32
|
||||
inform(destroy::<$($var,)* R> as u32);
|
||||
|
||||
<&Self>::describe();
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,11 +361,6 @@ macro_rules! doit {
|
||||
R: ReturnWasmAbi + 'static,
|
||||
{
|
||||
fn describe() {
|
||||
<&mut Self>::describe();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn invoke_fn() -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
@ -402,12 +385,10 @@ macro_rules! doit {
|
||||
};
|
||||
ret.return_abi(&mut GlobalStack::new())
|
||||
}
|
||||
invoke::<$($var,)* R> as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn destroy_fn() -> u32 {
|
||||
unsafe extern "C" fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
inform(invoke::<$($var,)* R> as u32);
|
||||
|
||||
unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
) {
|
||||
@ -416,7 +397,9 @@ macro_rules! doit {
|
||||
fields: (a, b)
|
||||
}.ptr));
|
||||
}
|
||||
destroy::<$($var,)* R> as u32
|
||||
inform(destroy::<$($var,)* R> as u32);
|
||||
|
||||
<&mut Self>::describe();
|
||||
}
|
||||
}
|
||||
)*)
|
||||
|
@ -1,93 +1,119 @@
|
||||
use core::mem;
|
||||
|
||||
use convert::{FromWasmAbi, GlobalStack, IntoWasmAbi, ReturnWasmAbi, Stack};
|
||||
use convert::slices::WasmSlice;
|
||||
use describe::{inform, FUNCTION, WasmDescribe};
|
||||
use throw_str;
|
||||
|
||||
macro_rules! stack_closures {
|
||||
($( ($($var:ident)*) )*) => ($(
|
||||
($( ($cnt:tt $invoke:ident $invoke_mut:ident $($var:ident)*) )*) => ($(
|
||||
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (Fn($($var),*) -> R + 'b)
|
||||
where $($var: FromWasmAbi,)*
|
||||
R: ReturnWasmAbi
|
||||
{
|
||||
type Abi = u32;
|
||||
type Abi = WasmSlice;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) -> <R as ReturnWasmAbi>::Abi {
|
||||
if a == 0 {
|
||||
throw_str("closure invoked recursively or destroyed already");
|
||||
}
|
||||
// Scope all local variables before we call `return_abi` to
|
||||
// ensure they're all destroyed as `return_abi` may throw
|
||||
let ret = {
|
||||
let f: &Fn($($var),*) -> R = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*)
|
||||
};
|
||||
ret.return_abi(&mut GlobalStack::new())
|
||||
}
|
||||
fn into_abi(self, _extra: &mut Stack) -> WasmSlice {
|
||||
unsafe {
|
||||
let (a, b): (usize, usize) = mem::transmute(self);
|
||||
extra.push(a as u32);
|
||||
extra.push(b as u32);
|
||||
invoke::<$($var,)* R> as u32
|
||||
WasmSlice { ptr: a as u32, len: b as u32 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn $invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) -> <R as ReturnWasmAbi>::Abi {
|
||||
if a == 0 {
|
||||
throw_str("closure invoked recursively or destroyed already");
|
||||
}
|
||||
// Scope all local variables before we call `return_abi` to
|
||||
// ensure they're all destroyed as `return_abi` may throw
|
||||
let ret = {
|
||||
let f: &Fn($($var),*) -> R = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*)
|
||||
};
|
||||
ret.return_abi(&mut GlobalStack::new())
|
||||
}
|
||||
|
||||
impl<'a, $($var,)* R> WasmDescribe for Fn($($var),*) -> R + 'a
|
||||
where $($var: FromWasmAbi,)*
|
||||
R: ReturnWasmAbi
|
||||
{
|
||||
fn describe() {
|
||||
inform(FUNCTION);
|
||||
inform($invoke::<$($var,)* R> as u32);
|
||||
inform($cnt);
|
||||
$(<$var as WasmDescribe>::describe();)*
|
||||
<R as WasmDescribe>::describe();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a mut (FnMut($($var),*) -> R + 'b)
|
||||
where $($var: FromWasmAbi,)*
|
||||
R: ReturnWasmAbi
|
||||
{
|
||||
type Abi = u32;
|
||||
type Abi = WasmSlice;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) -> <R as ReturnWasmAbi>::Abi {
|
||||
if a == 0 {
|
||||
throw_str("closure invoked recursively or destroyed already");
|
||||
}
|
||||
// Scope all local variables before we call `return_abi` to
|
||||
// ensure they're all destroyed as `return_abi` may throw
|
||||
let ret = {
|
||||
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*)
|
||||
};
|
||||
ret.return_abi(&mut GlobalStack::new())
|
||||
}
|
||||
fn into_abi(self, _extra: &mut Stack) -> WasmSlice {
|
||||
unsafe {
|
||||
let (a, b): (usize, usize) = mem::transmute(self);
|
||||
extra.push(a as u32);
|
||||
extra.push(b as u32);
|
||||
invoke::<$($var,)* R> as u32
|
||||
WasmSlice { ptr: a as u32, len: b as u32 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn $invoke_mut<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) -> <R as ReturnWasmAbi>::Abi {
|
||||
if a == 0 {
|
||||
throw_str("closure invoked recursively or destroyed already");
|
||||
}
|
||||
// Scope all local variables before we call `return_abi` to
|
||||
// ensure they're all destroyed as `return_abi` may throw
|
||||
let ret = {
|
||||
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*)
|
||||
};
|
||||
ret.return_abi(&mut GlobalStack::new())
|
||||
}
|
||||
|
||||
impl<'a, $($var,)* R> WasmDescribe for FnMut($($var),*) -> R + 'a
|
||||
where $($var: FromWasmAbi,)*
|
||||
R: ReturnWasmAbi
|
||||
{
|
||||
fn describe() {
|
||||
inform(FUNCTION);
|
||||
inform($invoke_mut::<$($var,)* R> as u32);
|
||||
inform($cnt);
|
||||
$(<$var as WasmDescribe>::describe();)*
|
||||
<R as WasmDescribe>::describe();
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
stack_closures! {
|
||||
()
|
||||
(A)
|
||||
(A B)
|
||||
(A B C)
|
||||
(A B C D)
|
||||
(A B C D E)
|
||||
(A B C D E F)
|
||||
(A B C D E F G)
|
||||
(0 invoke0 invoke0_mut)
|
||||
(1 invoke1 invoke1_mut A)
|
||||
(2 invoke2 invoke2_mut A B)
|
||||
(3 invoke3 invoke3_mut A B C)
|
||||
(4 invoke4 invoke4_mut A B C D)
|
||||
(5 invoke5 invoke5_mut A B C D E)
|
||||
(6 invoke6 invoke6_mut A B C D E F)
|
||||
(7 invoke7 invoke7_mut A B C D E F G)
|
||||
}
|
||||
|
@ -133,72 +133,6 @@ if_std! {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cnt {
|
||||
() => {
|
||||
0
|
||||
};
|
||||
(A) => {
|
||||
1
|
||||
};
|
||||
(A B) => {
|
||||
2
|
||||
};
|
||||
(A B C) => {
|
||||
3
|
||||
};
|
||||
(A B C D) => {
|
||||
4
|
||||
};
|
||||
(A B C D E) => {
|
||||
5
|
||||
};
|
||||
(A B C D E F) => {
|
||||
6
|
||||
};
|
||||
(A B C D E F G) => {
|
||||
7
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! doit {
|
||||
($( ($($var:ident)*))*) => ($(
|
||||
impl<'a, $($var,)* R> WasmDescribe for Fn($($var),*) -> R + 'a
|
||||
where $($var: WasmDescribe,)*
|
||||
R: WasmDescribe
|
||||
{
|
||||
fn describe() {
|
||||
inform(FUNCTION);
|
||||
inform(cnt!($($var)*));
|
||||
$(<$var as WasmDescribe>::describe();)*
|
||||
<R as WasmDescribe>::describe();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, $($var,)* R> WasmDescribe for FnMut($($var),*) -> R + 'a
|
||||
where $($var: WasmDescribe,)*
|
||||
R: WasmDescribe
|
||||
{
|
||||
fn describe() {
|
||||
inform(FUNCTION);
|
||||
inform(cnt!($($var)*));
|
||||
$(<$var as WasmDescribe>::describe();)*
|
||||
<R as WasmDescribe>::describe();
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
doit! {
|
||||
()
|
||||
(A)
|
||||
(A B)
|
||||
(A B C)
|
||||
(A B C D)
|
||||
(A B C D E)
|
||||
(A B C D E F)
|
||||
(A B C D E F G)
|
||||
}
|
||||
|
||||
impl<T: WasmDescribe> WasmDescribe for Option<T> {
|
||||
fn describe() {
|
||||
inform(OPTIONAL);
|
||||
|
@ -483,7 +483,7 @@ externs! {
|
||||
fn __wbindgen_cb_forget(idx: u32) -> ();
|
||||
|
||||
fn __wbindgen_describe(v: u32) -> ();
|
||||
fn __wbindgen_describe_closure(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32;
|
||||
fn __wbindgen_describe_closure(a: u32, b: u32, c: u32) -> u32;
|
||||
|
||||
fn __wbindgen_json_parse(ptr: *const u8, len: usize) -> u32;
|
||||
fn __wbindgen_json_serialize(idx: u32, ptr: *mut *mut u8) -> usize;
|
||||
|
Loading…
x
Reference in New Issue
Block a user