mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-25 19:11:45 +03:00
Support passing JS objects through Rust
This commit is contained in:
parent
eda9beae25
commit
946e5317fe
18
README.md
18
README.md
@ -17,10 +17,10 @@ Notable features of this project includes:
|
|||||||
* Exposing Rust functions to JS
|
* Exposing Rust functions to JS
|
||||||
* Managing arguments between JS/Rust (strings, numbers, classes, etc)
|
* Managing arguments between JS/Rust (strings, numbers, classes, etc)
|
||||||
* Importing JS functions with richer types (strings)
|
* Importing JS functions with richer types (strings)
|
||||||
|
* Receiving arbitrary JS objects in Rust, passing them through to JS
|
||||||
|
|
||||||
Planned features include:
|
Planned features include:
|
||||||
|
|
||||||
* Receiving arbitrary JS objects in Rust
|
|
||||||
* An optional flag to generate Typescript bindings
|
* An optional flag to generate Typescript bindings
|
||||||
* Field setters/getters in JS through Rust functions
|
* Field setters/getters in JS through Rust functions
|
||||||
* ... and more coming soon!
|
* ... and more coming soon!
|
||||||
@ -240,20 +240,21 @@ wasm_bindgen! {
|
|||||||
|
|
||||||
pub struct Bar {
|
pub struct Bar {
|
||||||
contents: u32,
|
contents: u32,
|
||||||
|
opaque: JsObject, // defined in `wasm_bindgen`, imported via prelude
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "JS" {
|
extern "JS" {
|
||||||
fn bar_on_reset(to: &str);
|
fn bar_on_reset(to: &str, opaque: &JsObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bar {
|
impl Bar {
|
||||||
pub fn from_str(s: &str) -> Bar {
|
pub fn from_str(s: &str, opaque: JsObject) -> Bar {
|
||||||
Bar { contents: s.parse().unwrap_or(0) }
|
Bar { contents: s.parse().unwrap_or(0), opaque }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, s: &str) {
|
pub fn reset(&mut self, s: &str) {
|
||||||
if let Ok(n) = s.parse() {
|
if let Ok(n) = s.parse() {
|
||||||
bar_on_reset(s);
|
bar_on_reset(s, &self.opaque);
|
||||||
self.contents = n;
|
self.contents = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,8 +283,9 @@ and this can be worked with similarly to above with:
|
|||||||
.then(bytes => {
|
.then(bytes => {
|
||||||
return instantiate(bytes, {
|
return instantiate(bytes, {
|
||||||
env: {
|
env: {
|
||||||
bar_on_reset(s) {
|
bar_on_reset(s, token) {
|
||||||
console.log(`an instance of bar was reset to ${s}`);
|
console.log(token);
|
||||||
|
console.log(`this instance of bar was reset to ${s}`);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -301,7 +303,7 @@ and this can be worked with similarly to above with:
|
|||||||
|
|
||||||
// Pass objects to one another
|
// Pass objects to one another
|
||||||
let foo1 = mod.Foo.new();
|
let foo1 = mod.Foo.new();
|
||||||
let bar = mod.Bar.from_str("22");
|
let bar = mod.Bar.from_str("22", { opaque: 'object' });
|
||||||
foo1.add_other(bar);
|
foo1.add_other(bar);
|
||||||
|
|
||||||
// We also don't have to `free` the `bar` variable as this function is
|
// We also don't have to `free` the `bar` variable as this function is
|
||||||
|
@ -55,6 +55,8 @@ pub fn project() -> Project {
|
|||||||
|
|
||||||
out.instantiate(wasm, test.imports).then(m => {
|
out.instantiate(wasm, test.imports).then(m => {
|
||||||
test.test(m);
|
test.test(m);
|
||||||
|
if (m.assertHeapAndStackEmpty)
|
||||||
|
m.assertHeapAndStackEmpty();
|
||||||
}).catch(function(error) {
|
}).catch(function(error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@ -146,6 +148,7 @@ impl Project {
|
|||||||
let obj = cli::Bindgen::new()
|
let obj = cli::Bindgen::new()
|
||||||
.input_path(&out)
|
.input_path(&out)
|
||||||
.nodejs(true)
|
.nodejs(true)
|
||||||
|
.debug(true)
|
||||||
.generate()
|
.generate()
|
||||||
.expect("failed to run bindgen");
|
.expect("failed to run bindgen");
|
||||||
obj.write_js_to(root.join("out.js")).expect("failed to write js");
|
obj.write_js_to(root.join("out.js")).expect("failed to write js");
|
||||||
|
@ -9,10 +9,12 @@ pub struct Js {
|
|||||||
expose_assert_num: bool,
|
expose_assert_num: bool,
|
||||||
expose_assert_class: bool,
|
expose_assert_class: bool,
|
||||||
expose_token: bool,
|
expose_token: bool,
|
||||||
|
expose_objects: bool,
|
||||||
exports: Vec<(String, String)>,
|
exports: Vec<(String, String)>,
|
||||||
classes: Vec<String>,
|
classes: Vec<String>,
|
||||||
imports: Vec<String>,
|
imports: Vec<String>,
|
||||||
pub nodejs: bool,
|
pub nodejs: bool,
|
||||||
|
pub debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Js {
|
impl Js {
|
||||||
@ -149,11 +151,31 @@ impl Js {
|
|||||||
", i = i, arg = name, struct_ = s));
|
", i = i, arg = name, struct_ = s));
|
||||||
pass(&format!("ptr{}", i));
|
pass(&format!("ptr{}", i));
|
||||||
}
|
}
|
||||||
|
shared::Type::JsObject => {
|
||||||
|
self.expose_objects = true;
|
||||||
|
arg_conversions.push_str(&format!("\
|
||||||
|
const idx{i} = addHeapObject({arg});
|
||||||
|
", i = i, arg = name));
|
||||||
|
pass(&format!("idx{}", i));
|
||||||
|
}
|
||||||
|
shared::Type::JsObjectRef => {
|
||||||
|
self.expose_objects = true;
|
||||||
|
arg_conversions.push_str(&format!("\
|
||||||
|
const idx{i} = addBorrowedObject({arg});
|
||||||
|
", i = i, arg = name));
|
||||||
|
destructors.push_str("popBorrowedObject();\n");
|
||||||
|
pass(&format!("idx{}", i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let convert_ret = match ret {
|
let convert_ret = match ret {
|
||||||
None |
|
None |
|
||||||
Some(&shared::Type::Number) => format!("return ret;"),
|
Some(&shared::Type::Number) => format!("return ret;"),
|
||||||
|
Some(&shared::Type::JsObject) => {
|
||||||
|
self.expose_objects = true;
|
||||||
|
format!("return takeObject(ret);")
|
||||||
|
}
|
||||||
|
Some(&shared::Type::JsObjectRef) |
|
||||||
Some(&shared::Type::BorrowedStr) |
|
Some(&shared::Type::BorrowedStr) |
|
||||||
Some(&shared::Type::ByMutRef(_)) |
|
Some(&shared::Type::ByMutRef(_)) |
|
||||||
Some(&shared::Type::ByRef(_)) => panic!(),
|
Some(&shared::Type::ByRef(_)) => panic!(),
|
||||||
@ -221,6 +243,16 @@ impl Js {
|
|||||||
invocation.push_str(&format!("getStringFromWasm(ptr{0}, len{0})", i));
|
invocation.push_str(&format!("getStringFromWasm(ptr{0}, len{0})", i));
|
||||||
dst.push_str(&format!("ptr{0}, len{0}", i));
|
dst.push_str(&format!("ptr{0}, len{0}", i));
|
||||||
}
|
}
|
||||||
|
shared::Type::JsObject => {
|
||||||
|
self.expose_objects = true;
|
||||||
|
invocation.push_str(&format!("takeObject(arg{})", i));
|
||||||
|
dst.push_str(&format!("arg{}", i));
|
||||||
|
}
|
||||||
|
shared::Type::JsObjectRef => {
|
||||||
|
self.expose_objects = true;
|
||||||
|
invocation.push_str(&format!("getObject(arg{})", i));
|
||||||
|
dst.push_str(&format!("arg{}", i));
|
||||||
|
}
|
||||||
shared::Type::String |
|
shared::Type::String |
|
||||||
shared::Type::ByRef(_) |
|
shared::Type::ByRef(_) |
|
||||||
shared::Type::ByMutRef(_) |
|
shared::Type::ByMutRef(_) |
|
||||||
@ -235,7 +267,7 @@ impl Js {
|
|||||||
self.imports.push(dst);
|
self.imports.push(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&mut self) -> String {
|
||||||
let mut globals = String::new();
|
let mut globals = String::new();
|
||||||
let mut real_globals = String::new();
|
let mut real_globals = String::new();
|
||||||
if self.expose_global_memory ||
|
if self.expose_global_memory ||
|
||||||
@ -328,6 +360,92 @@ impl Js {
|
|||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if self.expose_objects {
|
||||||
|
real_globals.push_str("
|
||||||
|
let stack = [];
|
||||||
|
let slab = [];
|
||||||
|
let slab_next = 0;
|
||||||
|
|
||||||
|
function addHeapObject(obj) {
|
||||||
|
if (slab_next == slab.length) {
|
||||||
|
slab.push(slab.length + 1);
|
||||||
|
}
|
||||||
|
const idx = slab_next;
|
||||||
|
slab_next = slab[idx];
|
||||||
|
slab[idx] = { obj, cnt: 1 };
|
||||||
|
return idx << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBorrowedObject(obj) {
|
||||||
|
stack.push(obj);
|
||||||
|
return ((stack.length - 1) << 1) | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function popBorrowedObject() {
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObject(idx) {
|
||||||
|
if (idx & 1 == 1) {
|
||||||
|
return stack[idx >> 1];
|
||||||
|
} else {
|
||||||
|
return slab[idx >> 1].obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function takeObject(idx) {
|
||||||
|
const ret = getObject(idx);
|
||||||
|
dropRef(idx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneRef(idx) {
|
||||||
|
// If this object is on the stack promote it to the heap.
|
||||||
|
if (idx & 1 == 1) {
|
||||||
|
return addHeapObject(getObject(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise if the object is on the heap just bump the
|
||||||
|
// refcount and move on
|
||||||
|
slab[idx >> 1].cnt += 1;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dropRef(idx) {
|
||||||
|
if (idx & 1 == 1)
|
||||||
|
throw new Error('cannot drop ref of stack objects');
|
||||||
|
|
||||||
|
// Decrement our refcount, but if it's still larger than one
|
||||||
|
// keep going
|
||||||
|
let obj = slab[idx >> 1];
|
||||||
|
obj.cnt -= 1;
|
||||||
|
if (obj.cnt > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If we hit 0 then free up our space in the slab
|
||||||
|
slab[idx >> 1] = slab_next;
|
||||||
|
slab_next = idx >> 1;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
|
||||||
|
if self.debug {
|
||||||
|
self.exports.push(
|
||||||
|
(
|
||||||
|
"assertHeapAndStackEmpty".to_string(),
|
||||||
|
"function() {
|
||||||
|
if (stack.length > 0)
|
||||||
|
throw new Error('stack is not empty');
|
||||||
|
for (let i = 0; i < slab.length; i++) {
|
||||||
|
if (typeof(slab[i]) !== 'number')
|
||||||
|
throw new Error('slab is not empty');
|
||||||
|
}
|
||||||
|
}".to_string(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut exports = String::new();
|
let mut exports = String::new();
|
||||||
for class in self.classes.iter() {
|
for class in self.classes.iter() {
|
||||||
exports.push_str(class);
|
exports.push_str(class);
|
||||||
@ -345,6 +463,13 @@ impl Js {
|
|||||||
imports.push_str(import);
|
imports.push_str(import);
|
||||||
imports.push_str("\n");
|
imports.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.expose_objects {
|
||||||
|
imports.push_str("
|
||||||
|
imports.env.__wasm_bindgen_object_clone_ref = cloneRef;
|
||||||
|
imports.env.__wasm_bindgen_object_drop_ref = dropRef;
|
||||||
|
");
|
||||||
|
}
|
||||||
format!("
|
format!("
|
||||||
{}
|
{}
|
||||||
function xform(obj) {{
|
function xform(obj) {{
|
||||||
|
@ -16,12 +16,14 @@ mod js;
|
|||||||
pub struct Bindgen {
|
pub struct Bindgen {
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
nodejs: bool,
|
nodejs: bool,
|
||||||
|
debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
module: Module,
|
module: Module,
|
||||||
program: shared::Program,
|
program: shared::Program,
|
||||||
nodejs: bool,
|
nodejs: bool,
|
||||||
|
debug: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bindgen {
|
impl Bindgen {
|
||||||
@ -29,6 +31,7 @@ impl Bindgen {
|
|||||||
Bindgen {
|
Bindgen {
|
||||||
path: None,
|
path: None,
|
||||||
nodejs: false,
|
nodejs: false,
|
||||||
|
debug: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +45,11 @@ impl Bindgen {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn debug(&mut self, debug: bool) -> &mut Bindgen {
|
||||||
|
self.debug = debug;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate(&mut self) -> Result<Object, Error> {
|
pub fn generate(&mut self) -> Result<Object, Error> {
|
||||||
let input = match self.path {
|
let input = match self.path {
|
||||||
Some(ref path) => path,
|
Some(ref path) => path,
|
||||||
@ -55,6 +63,7 @@ impl Bindgen {
|
|||||||
module,
|
module,
|
||||||
program,
|
program,
|
||||||
nodejs: self.nodejs,
|
nodejs: self.nodejs,
|
||||||
|
debug: self.debug,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,6 +98,7 @@ impl Object {
|
|||||||
pub fn generate_js(&self) -> String {
|
pub fn generate_js(&self) -> String {
|
||||||
let mut js = js::Js::default();
|
let mut js = js::Js::default();
|
||||||
js.nodejs = self.nodejs;
|
js.nodejs = self.nodejs;
|
||||||
|
js.debug = self.debug;
|
||||||
js.generate_program(&self.program);
|
js.generate_program(&self.program);
|
||||||
js.to_string()
|
js.to_string()
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ pub enum Type {
|
|||||||
ByValue(syn::Ident),
|
ByValue(syn::Ident),
|
||||||
ByRef(syn::Ident),
|
ByRef(syn::Ident),
|
||||||
ByMutRef(syn::Ident),
|
ByMutRef(syn::Ident),
|
||||||
|
JsObject,
|
||||||
|
JsObjectRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
@ -227,6 +229,10 @@ impl Type {
|
|||||||
}
|
}
|
||||||
Type::BorrowedStr
|
Type::BorrowedStr
|
||||||
}
|
}
|
||||||
|
"JsObject" if !mutable => Type::JsObjectRef,
|
||||||
|
"JsObject" if mutable => {
|
||||||
|
panic!("can't have mutable js object refs")
|
||||||
|
}
|
||||||
_ if mutable => Type::ByMutRef(ident),
|
_ if mutable => Type::ByMutRef(ident),
|
||||||
_ => Type::ByRef(ident),
|
_ => Type::ByRef(ident),
|
||||||
}
|
}
|
||||||
@ -250,6 +256,7 @@ impl Type {
|
|||||||
Type::Integer(ident)
|
Type::Integer(ident)
|
||||||
}
|
}
|
||||||
"String" => Type::String,
|
"String" => Type::String,
|
||||||
|
"JsObject" => Type::JsObject,
|
||||||
_ => Type::ByValue(ident),
|
_ => Type::ByValue(ident),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,6 +272,8 @@ impl Type {
|
|||||||
Type::ByValue(n) => shared::Type::ByValue(n.to_string()),
|
Type::ByValue(n) => shared::Type::ByValue(n.to_string()),
|
||||||
Type::ByRef(n) => shared::Type::ByRef(n.to_string()),
|
Type::ByRef(n) => shared::Type::ByRef(n.to_string()),
|
||||||
Type::ByMutRef(n) => shared::Type::ByMutRef(n.to_string()),
|
Type::ByMutRef(n) => shared::Type::ByMutRef(n.to_string()),
|
||||||
|
Type::JsObject => shared::Type::JsObject,
|
||||||
|
Type::JsObjectRef => shared::Type::JsObjectRef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
|||||||
*#generated_static_value;
|
*#generated_static_value;
|
||||||
}).to_tokens(&mut ret);
|
}).to_tokens(&mut ret);
|
||||||
|
|
||||||
println!("{}", ret);
|
// println!("{}", ret);
|
||||||
|
|
||||||
ret.into()
|
ret.into()
|
||||||
}
|
}
|
||||||
@ -241,6 +241,21 @@ fn bindgen(export_name: &syn::Lit,
|
|||||||
let #ident = &mut *#ident;
|
let #ident = &mut *#ident;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
ast::Type::JsObject => {
|
||||||
|
args.push(my_quote! { #ident: u32 });
|
||||||
|
arg_conversions.push(my_quote! {
|
||||||
|
let #ident = ::wasm_bindgen::JsObject::__from_idx(#ident);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ast::Type::JsObjectRef => {
|
||||||
|
args.push(my_quote! { #ident: u32 });
|
||||||
|
arg_conversions.push(my_quote! {
|
||||||
|
let #ident = ::std::mem::ManuallyDrop::new(
|
||||||
|
::wasm_bindgen::JsObject::__from_idx(#ident)
|
||||||
|
);
|
||||||
|
let #ident = &*#ident;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
converted_arguments.push(my_quote! { #ident });
|
converted_arguments.push(my_quote! { #ident });
|
||||||
}
|
}
|
||||||
@ -265,6 +280,15 @@ fn bindgen(export_name: &syn::Lit,
|
|||||||
Box::into_raw(Box::new(::std::cell::RefCell::new(#ret)))
|
Box::into_raw(Box::new(::std::cell::RefCell::new(#ret)))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Some(&ast::Type::JsObject) => {
|
||||||
|
ret_ty = my_quote! { -> u32 };
|
||||||
|
convert_ret = my_quote! {
|
||||||
|
::wasm_bindgen::JsObject::__into_idx(#ret)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(&ast::Type::JsObjectRef) => {
|
||||||
|
panic!("can't return a borrowed ref");
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
ret_ty = my_quote! {};
|
ret_ty = my_quote! {};
|
||||||
convert_ret = my_quote! {};
|
convert_ret = my_quote! {};
|
||||||
@ -407,11 +431,25 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
|||||||
let #len = #name.len();
|
let #len = #name.len();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
ast::Type::JsObject => {
|
||||||
|
abi_argument_names.push(name);
|
||||||
|
abi_arguments.push(my_quote! { #name: u32 });
|
||||||
|
arg_conversions.push(my_quote! {
|
||||||
|
let #name = ::wasm_bindgen::JsObject::__into_idx(#name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ast::Type::JsObjectRef => {
|
||||||
|
abi_argument_names.push(name);
|
||||||
|
abi_arguments.push(my_quote! { #name: u32 });
|
||||||
|
arg_conversions.push(my_quote! {
|
||||||
|
let #name = ::wasm_bindgen::JsObject::__get_idx(#name);
|
||||||
|
});
|
||||||
|
}
|
||||||
ast::Type::String => panic!("can't use `String` in foreign functions"),
|
ast::Type::String => panic!("can't use `String` in foreign functions"),
|
||||||
ast::Type::ByValue(_name) |
|
ast::Type::ByValue(_name) |
|
||||||
ast::Type::ByRef(_name) |
|
ast::Type::ByRef(_name) |
|
||||||
ast::Type::ByMutRef(_name) => {
|
ast::Type::ByMutRef(_name) => {
|
||||||
panic!("can't use strct types in foreign functions yet");
|
panic!("can't use struct types in foreign functions yet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,6 +460,13 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
|||||||
abi_ret = my_quote! { #i };
|
abi_ret = my_quote! { #i };
|
||||||
convert_ret = my_quote! { #ret_ident };
|
convert_ret = my_quote! { #ret_ident };
|
||||||
}
|
}
|
||||||
|
Some(ast::Type::JsObject) => {
|
||||||
|
abi_ret = my_quote! { u32 };
|
||||||
|
convert_ret = my_quote! {
|
||||||
|
::wasm_bindgen::JsObject::__from_idx(#ret_ident)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(ast::Type::JsObjectRef) => panic!("can't return a borrowed ref"),
|
||||||
Some(ast::Type::BorrowedStr) => panic!("can't return a borrowed string"),
|
Some(ast::Type::BorrowedStr) => panic!("can't return a borrowed string"),
|
||||||
Some(ast::Type::ByRef(_)) => panic!("can't return a borrowed ref"),
|
Some(ast::Type::ByRef(_)) => panic!("can't return a borrowed ref"),
|
||||||
Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
|
Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
|
||||||
|
@ -63,6 +63,8 @@ pub enum Type {
|
|||||||
ByValue(String),
|
ByValue(String),
|
||||||
ByRef(String),
|
ByRef(String),
|
||||||
ByMutRef(String),
|
ByMutRef(String),
|
||||||
|
JsObject,
|
||||||
|
JsObjectRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
|
47
src/lib.rs
47
src/lib.rs
@ -2,11 +2,54 @@
|
|||||||
|
|
||||||
extern crate wasm_bindgen_macro;
|
extern crate wasm_bindgen_macro;
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use wasm_bindgen_macro::wasm_bindgen;
|
pub use wasm_bindgen_macro::wasm_bindgen;
|
||||||
pub use Object;
|
pub use JsObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Object {
|
pub struct JsObject {
|
||||||
idx: u32,
|
idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JsObject {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn __from_idx(idx: u32) -> JsObject {
|
||||||
|
JsObject { idx }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn __get_idx(&self) -> u32 {
|
||||||
|
self.idx
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn __into_idx(self) -> u32 {
|
||||||
|
let ret = self.idx;
|
||||||
|
mem::forget(self);
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern {
|
||||||
|
fn __wasm_bindgen_object_clone_ref(idx: u32) -> u32;
|
||||||
|
fn __wasm_bindgen_object_drop_ref(idx: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for JsObject {
|
||||||
|
fn clone(&self) -> JsObject {
|
||||||
|
unsafe {
|
||||||
|
let idx = __wasm_bindgen_object_clone_ref(self.idx);
|
||||||
|
JsObject { idx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for JsObject {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
__wasm_bindgen_object_drop_ref(self.idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
184
tests/jsobjects.rs
Normal file
184
tests/jsobjects.rs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
extern crate test_support;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple() {
|
||||||
|
test_support::project()
|
||||||
|
.file("src/lib.rs", r#"
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
wasm_bindgen! {
|
||||||
|
extern "JS" {
|
||||||
|
fn foo(s: &JsObject);
|
||||||
|
}
|
||||||
|
pub fn bar(s: &JsObject) {
|
||||||
|
foo(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.file("test.js", r#"
|
||||||
|
import * as assert from "assert";
|
||||||
|
|
||||||
|
let ARG = null;
|
||||||
|
|
||||||
|
export const imports = {
|
||||||
|
env: {
|
||||||
|
foo(s) {
|
||||||
|
assert.strictEqual(ARG, null);
|
||||||
|
ARG = s;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function test(wasm) {
|
||||||
|
assert.strictEqual(ARG, null);
|
||||||
|
let sym = Symbol('test');
|
||||||
|
wasm.bar(sym);
|
||||||
|
assert.strictEqual(ARG, sym);
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn owned() {
|
||||||
|
test_support::project()
|
||||||
|
.file("src/lib.rs", r#"
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
wasm_bindgen! {
|
||||||
|
extern "JS" {
|
||||||
|
fn foo(s: JsObject);
|
||||||
|
}
|
||||||
|
pub fn bar(s: JsObject) {
|
||||||
|
foo(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.file("test.js", r#"
|
||||||
|
import * as assert from "assert";
|
||||||
|
|
||||||
|
let ARG = null;
|
||||||
|
|
||||||
|
export const imports = {
|
||||||
|
env: {
|
||||||
|
foo(s) {
|
||||||
|
assert.strictEqual(ARG, null);
|
||||||
|
ARG = s;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function test(wasm) {
|
||||||
|
assert.strictEqual(ARG, null);
|
||||||
|
let sym = Symbol('test');
|
||||||
|
wasm.bar(sym);
|
||||||
|
assert.strictEqual(ARG, sym);
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clone() {
|
||||||
|
test_support::project()
|
||||||
|
.file("src/lib.rs", r#"
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
wasm_bindgen! {
|
||||||
|
extern "JS" {
|
||||||
|
fn foo1(s: JsObject);
|
||||||
|
fn foo2(s: &JsObject);
|
||||||
|
fn foo3(s: JsObject);
|
||||||
|
fn foo4(s: &JsObject);
|
||||||
|
fn foo5(s: JsObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bar(s: JsObject) {
|
||||||
|
foo1(s.clone());
|
||||||
|
foo2(&s);
|
||||||
|
foo3(s.clone());
|
||||||
|
foo4(&s);
|
||||||
|
foo5(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.file("test.js", r#"
|
||||||
|
import * as assert from "assert";
|
||||||
|
|
||||||
|
let ARG = Symbol('test');
|
||||||
|
|
||||||
|
export const imports = {
|
||||||
|
env: {
|
||||||
|
foo1(s) { assert.strictEqual(s, ARG); },
|
||||||
|
foo2(s) { assert.strictEqual(s, ARG); },
|
||||||
|
foo3(s) { assert.strictEqual(s, ARG); },
|
||||||
|
foo4(s) { assert.strictEqual(s, ARG); },
|
||||||
|
foo5(s) { assert.strictEqual(s, ARG); },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function test(wasm) {
|
||||||
|
wasm.bar(ARG);
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn promote() {
|
||||||
|
test_support::project()
|
||||||
|
.file("src/lib.rs", r#"
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
wasm_bindgen! {
|
||||||
|
extern "JS" {
|
||||||
|
fn foo1(s: &JsObject);
|
||||||
|
fn foo2(s: JsObject);
|
||||||
|
fn foo3(s: &JsObject);
|
||||||
|
fn foo4(s: JsObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bar(s: &JsObject) {
|
||||||
|
foo1(s);
|
||||||
|
foo2(s.clone());
|
||||||
|
foo3(s);
|
||||||
|
foo4(s.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.file("test.js", r#"
|
||||||
|
import * as assert from "assert";
|
||||||
|
|
||||||
|
let ARG = Symbol('test');
|
||||||
|
|
||||||
|
export const imports = {
|
||||||
|
env: {
|
||||||
|
foo1(s) { assert.strictEqual(s, ARG); },
|
||||||
|
foo2(s) { assert.strictEqual(s, ARG); },
|
||||||
|
foo3(s) { assert.strictEqual(s, ARG); },
|
||||||
|
foo4(s) { assert.strictEqual(s, ARG); },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function test(wasm) {
|
||||||
|
wasm.bar(ARG);
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.test();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user