mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-27 03:55:20 +03:00
Greatly simplify handling of types in Rust
Push the compiler to do trait resolution to figure out what each type is bound with in JS, and that way we can accept effectively all types (so long as they implement a trait).
This commit is contained in:
parent
0320bc0d7c
commit
d5ff725913
@ -21,7 +21,7 @@ impl<'a> Js<'a> {
|
||||
self.generate_free_function(f);
|
||||
}
|
||||
for f in self.program.imports.iter() {
|
||||
self.generate_import(&f.0, &f.1);
|
||||
self.generate_import(&f.module, &f.function);
|
||||
}
|
||||
for s in self.program.structs.iter() {
|
||||
self.generate_struct(s);
|
||||
@ -238,14 +238,14 @@ impl<'a> Js<'a> {
|
||||
this.ptr = 0;
|
||||
wasm.{}(ptr);
|
||||
}}
|
||||
", s.free_function()));
|
||||
", shared::free_function(&s.name)));
|
||||
ts_dst.push_str("free(): void;\n");
|
||||
|
||||
for function in s.functions.iter() {
|
||||
let (js, ts) = self.generate_function(
|
||||
"static",
|
||||
&function.name,
|
||||
&function.struct_function_export_name(&s.name),
|
||||
&shared::struct_function_export_name(&s.name, &function.name),
|
||||
false,
|
||||
&function.arguments,
|
||||
function.ret.as_ref(),
|
||||
@ -259,7 +259,7 @@ impl<'a> Js<'a> {
|
||||
let (js, ts) = self.generate_function(
|
||||
"",
|
||||
&method.function.name,
|
||||
&method.function.struct_function_export_name(&s.name),
|
||||
&shared::struct_function_export_name(&s.name, &method.function.name),
|
||||
true,
|
||||
&method.function.arguments,
|
||||
method.function.ret.as_ref(),
|
||||
@ -311,7 +311,7 @@ impl<'a> Js<'a> {
|
||||
passed_args.push_str(arg);
|
||||
};
|
||||
match *arg {
|
||||
shared::Type::Number => {
|
||||
shared::TYPE_NUMBER => {
|
||||
dst_ts.push_str(": number");
|
||||
if self.config.debug {
|
||||
self.expose_assert_num();
|
||||
@ -319,7 +319,7 @@ impl<'a> Js<'a> {
|
||||
}
|
||||
pass(&name)
|
||||
}
|
||||
shared::Type::Boolean => {
|
||||
shared::TYPE_BOOLEAN => {
|
||||
dst_ts.push_str(": boolean");
|
||||
if self.config.debug {
|
||||
self.expose_assert_bool();
|
||||
@ -330,8 +330,8 @@ impl<'a> Js<'a> {
|
||||
}
|
||||
pass(&format!("arg{i} ? 1 : 0", i = i))
|
||||
}
|
||||
shared::Type::BorrowedStr |
|
||||
shared::Type::String => {
|
||||
shared::TYPE_BORROWED_STR |
|
||||
shared::TYPE_STRING => {
|
||||
dst_ts.push_str(": string");
|
||||
self.expose_pass_string_to_wasm();
|
||||
arg_conversions.push_str(&format!("\
|
||||
@ -339,14 +339,33 @@ impl<'a> Js<'a> {
|
||||
", i = i, arg = name));
|
||||
pass(&format!("ptr{}", i));
|
||||
pass(&format!("len{}", i));
|
||||
if let shared::Type::BorrowedStr = *arg {
|
||||
if *arg == shared::TYPE_BORROWED_STR {
|
||||
destructors.push_str(&format!("\n\
|
||||
wasm.__wbindgen_free(ptr{i}, len{i});\n\
|
||||
", i = i));
|
||||
}
|
||||
}
|
||||
shared::Type::ByRef(ref s) |
|
||||
shared::Type::ByMutRef(ref s) => {
|
||||
shared::TYPE_JS_OWNED => {
|
||||
dst_ts.push_str(": any");
|
||||
self.expose_add_heap_object();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addHeapObject({arg});
|
||||
", i = i, arg = name));
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
shared::TYPE_JS_REF => {
|
||||
dst_ts.push_str(": any");
|
||||
self.expose_borrowed_objects();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addBorrowedObject({arg});
|
||||
", i = i, arg = name));
|
||||
destructors.push_str("stack.pop();\n");
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
custom if (custom as u32) & shared::TYPE_CUSTOM_REF_FLAG != 0 => {
|
||||
let custom = ((custom as u32) & !shared::TYPE_CUSTOM_REF_FLAG) -
|
||||
shared::TYPE_CUSTOM_START;
|
||||
let s = &self.program.custom_type_names[custom as usize / 2];
|
||||
dst_ts.push_str(&format!(": {}", s));
|
||||
if self.config.debug {
|
||||
self.expose_assert_class();
|
||||
@ -356,7 +375,9 @@ impl<'a> Js<'a> {
|
||||
}
|
||||
pass(&format!("{}.ptr", name));
|
||||
}
|
||||
shared::Type::ByValue(ref s) => {
|
||||
custom => {
|
||||
let custom = (custom as u32) - shared::TYPE_CUSTOM_START;
|
||||
let s = &self.program.custom_type_names[custom as usize / 2];
|
||||
dst_ts.push_str(&format!(": {}", s));
|
||||
if self.config.debug {
|
||||
self.expose_assert_class();
|
||||
@ -370,23 +391,6 @@ impl<'a> Js<'a> {
|
||||
", i = i, arg = name));
|
||||
pass(&format!("ptr{}", i));
|
||||
}
|
||||
shared::Type::JsObject => {
|
||||
dst_ts.push_str(": any");
|
||||
self.expose_add_heap_object();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addHeapObject({arg});
|
||||
", i = i, arg = name));
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
shared::Type::JsObjectRef => {
|
||||
dst_ts.push_str(": any");
|
||||
self.expose_borrowed_objects();
|
||||
arg_conversions.push_str(&format!("\
|
||||
const idx{i} = addBorrowedObject({arg});
|
||||
", i = i, arg = name));
|
||||
destructors.push_str("stack.pop();\n");
|
||||
pass(&format!("idx{}", i));
|
||||
}
|
||||
}
|
||||
}
|
||||
dst.push_str(")");
|
||||
@ -396,24 +400,36 @@ impl<'a> Js<'a> {
|
||||
dst_ts.push_str(": void");
|
||||
format!("return ret;")
|
||||
}
|
||||
Some(&shared::Type::Number) => {
|
||||
Some(&shared::TYPE_NUMBER) => {
|
||||
dst_ts.push_str(": number");
|
||||
format!("return ret;")
|
||||
}
|
||||
Some(&shared::Type::Boolean) => {
|
||||
Some(&shared::TYPE_BOOLEAN) => {
|
||||
dst_ts.push_str(": boolean");
|
||||
format!("return ret != 0;")
|
||||
}
|
||||
Some(&shared::Type::JsObject) => {
|
||||
Some(&shared::TYPE_JS_OWNED) => {
|
||||
dst_ts.push_str(": any");
|
||||
self.expose_take_object();
|
||||
format!("return takeObject(ret);")
|
||||
}
|
||||
Some(&shared::Type::JsObjectRef) |
|
||||
Some(&shared::Type::BorrowedStr) |
|
||||
Some(&shared::Type::ByMutRef(_)) |
|
||||
Some(&shared::Type::ByRef(_)) => panic!(),
|
||||
Some(&shared::Type::ByValue(ref name)) => {
|
||||
Some(&shared::TYPE_STRING) => {
|
||||
dst_ts.push_str(": string");
|
||||
self.expose_get_string_from_wasm();
|
||||
format!("
|
||||
const ptr = wasm.__wbindgen_boxed_str_ptr(ret);
|
||||
const len = wasm.__wbindgen_boxed_str_len(ret);
|
||||
const realRet = getStringFromWasm(ptr, len);
|
||||
wasm.__wbindgen_boxed_str_free(ret);
|
||||
return realRet;
|
||||
")
|
||||
}
|
||||
Some(&shared::TYPE_JS_REF) |
|
||||
Some(&shared::TYPE_BORROWED_STR) => panic!(),
|
||||
Some(&t) if (t as u32) & shared::TYPE_CUSTOM_REF_FLAG != 0 => panic!(),
|
||||
Some(&custom) => {
|
||||
let custom = (custom as u32) - shared::TYPE_CUSTOM_START;
|
||||
let name = &self.program.custom_type_names[custom as usize / 2];
|
||||
dst_ts.push_str(": ");
|
||||
dst_ts.push_str(name);
|
||||
if self.config.debug {
|
||||
@ -426,17 +442,6 @@ impl<'a> Js<'a> {
|
||||
", name = name)
|
||||
}
|
||||
}
|
||||
Some(&shared::Type::String) => {
|
||||
dst_ts.push_str(": string");
|
||||
self.expose_get_string_from_wasm();
|
||||
format!("
|
||||
const ptr = wasm.__wbindgen_boxed_str_ptr(ret);
|
||||
const len = wasm.__wbindgen_boxed_str_len(ret);
|
||||
const realRet = getStringFromWasm(ptr, len);
|
||||
wasm.__wbindgen_boxed_str_free(ret);
|
||||
return realRet;
|
||||
")
|
||||
}
|
||||
};
|
||||
dst_ts.push_str(";");
|
||||
dst.push_str(" {\n ");
|
||||
@ -476,7 +481,7 @@ impl<'a> Js<'a> {
|
||||
import {{ {} as {} }} from '{}';
|
||||
", import.name, imported_name, module));
|
||||
|
||||
self.gen_import_shim(&import.mangled_import_name(None),
|
||||
self.gen_import_shim(&shared::mangled_import_name(None, &import.name),
|
||||
&imported_name,
|
||||
import)
|
||||
}
|
||||
@ -488,10 +493,10 @@ impl<'a> Js<'a> {
|
||||
", import.name, module));
|
||||
}
|
||||
|
||||
for &(method, ref function) in import.functions.iter() {
|
||||
self.generate_import_struct_function(&import.name,
|
||||
method,
|
||||
function);
|
||||
for import in import.functions.iter() {
|
||||
self.generate_import_struct_function(&import.function.name,
|
||||
import.method,
|
||||
&import.function);
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,7 +511,7 @@ impl<'a> Js<'a> {
|
||||
} else {
|
||||
format!("{}.{}", class, function.name)
|
||||
};
|
||||
self.gen_import_shim(&function.mangled_import_name(Some(class)),
|
||||
self.gen_import_shim(&shared::mangled_import_name(Some(class), &function.name),
|
||||
&delegate,
|
||||
function)
|
||||
}
|
||||
@ -530,33 +535,30 @@ impl<'a> Js<'a> {
|
||||
dst.push_str(", ");
|
||||
}
|
||||
match *arg {
|
||||
shared::Type::Number => {
|
||||
shared::TYPE_NUMBER => {
|
||||
invocation.push_str(&format!("arg{}", i));
|
||||
dst.push_str(&format!("arg{}", i));
|
||||
}
|
||||
shared::Type::Boolean => {
|
||||
shared::TYPE_BOOLEAN => {
|
||||
invocation.push_str(&format!("arg{} != 0", i));
|
||||
dst.push_str(&format!("arg{}", i));
|
||||
}
|
||||
shared::Type::BorrowedStr => {
|
||||
shared::TYPE_BORROWED_STR => {
|
||||
self.expose_get_string_from_wasm();
|
||||
invocation.push_str(&format!("getStringFromWasm(ptr{0}, len{0})", i));
|
||||
dst.push_str(&format!("ptr{0}, len{0}", i));
|
||||
}
|
||||
shared::Type::JsObject => {
|
||||
shared::TYPE_JS_OWNED => {
|
||||
self.expose_take_object();
|
||||
invocation.push_str(&format!("takeObject(arg{})", i));
|
||||
dst.push_str(&format!("arg{}", i));
|
||||
}
|
||||
shared::Type::JsObjectRef => {
|
||||
shared::TYPE_JS_REF => {
|
||||
self.expose_get_object();
|
||||
invocation.push_str(&format!("getObject(arg{})", i));
|
||||
dst.push_str(&format!("arg{}", i));
|
||||
}
|
||||
shared::Type::String |
|
||||
shared::Type::ByRef(_) |
|
||||
shared::Type::ByMutRef(_) |
|
||||
shared::Type::ByValue(_) => {
|
||||
_ => {
|
||||
panic!("unsupported type in import");
|
||||
}
|
||||
}
|
||||
@ -564,9 +566,9 @@ impl<'a> Js<'a> {
|
||||
dst.push_str(")");
|
||||
let invoc = format!("{}({})", shim_delegate, invocation);
|
||||
let invoc = match import.ret {
|
||||
Some(shared::Type::Number) => invoc,
|
||||
Some(shared::Type::Boolean) => format!("{} ? 1 : 0", invoc),
|
||||
Some(shared::Type::JsObject) => {
|
||||
Some(shared::TYPE_NUMBER) => invoc,
|
||||
Some(shared::TYPE_BOOLEAN) => format!("{} ? 1 : 0", invoc),
|
||||
Some(shared::TYPE_JS_OWNED) => {
|
||||
self.expose_add_heap_object();
|
||||
format!("addHeapObject({})", invoc)
|
||||
}
|
||||
@ -615,11 +617,13 @@ impl<'a> Js<'a> {
|
||||
// fixed upstream.
|
||||
let program_import = self.program.imports
|
||||
.iter()
|
||||
.any(|&(_, ref f)| f.mangled_import_name(None) == import.field());
|
||||
.any(|f| shared::mangled_import_name(None, &f.function.name) == import.field());
|
||||
let struct_import = self.program.imported_structs
|
||||
.iter()
|
||||
.flat_map(|s| s.functions.iter().map(move |f| (s, &f.1)))
|
||||
.any(|(s, f)| f.mangled_import_name(Some(&s.name)) == import.field());
|
||||
.flat_map(|s| s.functions.iter().map(move |f| (s, &f.function)))
|
||||
.any(|(s, f)| {
|
||||
shared::mangled_import_name(Some(&s.name), &f.name) == import.field()
|
||||
});
|
||||
if program_import || struct_import {
|
||||
import.module_mut().truncate(0);
|
||||
import.module_mut().push_str("./");
|
||||
|
@ -110,6 +110,7 @@ fn extract_program(module: &mut Module) -> shared::Program {
|
||||
free_functions: Vec::new(),
|
||||
imports: Vec::new(),
|
||||
imported_structs: Vec::new(),
|
||||
custom_type_names: Vec::new(),
|
||||
};
|
||||
let data = match data {
|
||||
Some(data) => data,
|
||||
@ -125,13 +126,25 @@ fn extract_program(module: &mut Module) -> shared::Program {
|
||||
let json = &value[4..];
|
||||
let p = match serde_json::from_slice(json) {
|
||||
Ok(f) => f,
|
||||
Err(_) => continue,
|
||||
Err(e) => {
|
||||
panic!("failed to decode what looked like wasm-bindgen data: {}", e)
|
||||
}
|
||||
};
|
||||
let shared::Program { structs, free_functions, imports, imported_structs } = p;
|
||||
let shared::Program {
|
||||
structs,
|
||||
free_functions,
|
||||
imports,
|
||||
imported_structs,
|
||||
custom_type_names,
|
||||
} = p;
|
||||
ret.structs.extend(structs);
|
||||
ret.free_functions.extend(free_functions);
|
||||
ret.imports.extend(imports);
|
||||
ret.imported_structs.extend(imported_structs);
|
||||
if custom_type_names.len() > 0 {
|
||||
assert_eq!(ret.custom_type_names.len(), 0);
|
||||
}
|
||||
ret.custom_type_names.extend(custom_type_names);
|
||||
}
|
||||
data.entries_mut().remove(i);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use proc_macro2::Span;
|
||||
use quote::{Tokens, ToTokens};
|
||||
use shared;
|
||||
use syn;
|
||||
use wasm_bindgen_shared as shared;
|
||||
|
||||
pub struct Program {
|
||||
pub structs: Vec<Struct>,
|
||||
@ -35,17 +36,13 @@ pub struct ImportStruct {
|
||||
}
|
||||
|
||||
pub enum Type {
|
||||
Integer(syn::Ident),
|
||||
// special
|
||||
BorrowedStr,
|
||||
String,
|
||||
ByValue(syn::Ident),
|
||||
ByRef(syn::Ident),
|
||||
ByMutRef(syn::Ident),
|
||||
RawMutPtr(syn::Ident),
|
||||
RawConstPtr(syn::Ident),
|
||||
JsObject,
|
||||
JsObjectRef,
|
||||
Boolean,
|
||||
|
||||
ByRef(syn::Type),
|
||||
ByMutRef(syn::Type),
|
||||
ByValue(syn::Type),
|
||||
}
|
||||
|
||||
pub struct Struct {
|
||||
@ -73,8 +70,13 @@ impl Program {
|
||||
if item.generics.params.len() > 0 {
|
||||
panic!("generic impls aren't supported");
|
||||
}
|
||||
let name = match Type::from(&item.self_ty) {
|
||||
Type::ByValue(ident) => ident,
|
||||
let name = match *item.self_ty {
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
||||
match extract_path_ident(path) {
|
||||
Some(ident) => ident,
|
||||
None => panic!("unsupported self type in impl"),
|
||||
}
|
||||
}
|
||||
_ => panic!("unsupported self type in impl"),
|
||||
};
|
||||
let dst = self.structs
|
||||
@ -162,17 +164,20 @@ impl Program {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn shared(&self) -> shared::Program {
|
||||
shared::Program {
|
||||
structs: self.structs.iter().map(|s| s.shared()).collect(),
|
||||
free_functions: self.free_functions.iter().map(|s| s.shared()).collect(),
|
||||
imports: self.imports.iter()
|
||||
.map(|i| (i.module.clone(), i.function.wasm_function.shared()))
|
||||
.collect(),
|
||||
imported_structs: self.imported_structs.iter()
|
||||
.map(|i| i.shared())
|
||||
.collect(),
|
||||
}
|
||||
pub fn wbg_literal(&self, dst: &mut Tokens) -> usize {
|
||||
let mut a = LiteralBuilder {
|
||||
dst,
|
||||
cnt: 0,
|
||||
};
|
||||
a.append("wbg:");
|
||||
a.fields(&[
|
||||
("structs", &|a| a.list(&self.structs, Struct::wbg_literal)),
|
||||
("free_functions", &|a| a.list(&self.free_functions, Function::wbg_literal)),
|
||||
("imports", &|a| a.list(&self.imports, Import::wbg_literal)),
|
||||
("imported_structs", &|a| a.list(&self.imported_structs, ImportStruct::wbg_literal)),
|
||||
("custom_type_names", &|a| a.list(&self.structs, |s, a| a.str(s.name.as_ref()))),
|
||||
]);
|
||||
return a.cnt
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,12 +241,15 @@ impl Function {
|
||||
}
|
||||
|
||||
pub fn free_function_export_name(&self) -> syn::LitStr {
|
||||
let name = self.shared().free_function_export_name();
|
||||
let name = shared::free_function_export_name(self.name.as_ref());
|
||||
syn::LitStr::new(&name, Span::def_site())
|
||||
}
|
||||
|
||||
pub fn struct_function_export_name(&self, s: syn::Ident) -> syn::LitStr {
|
||||
let name = self.shared().struct_function_export_name(s.as_ref());
|
||||
let name = shared::struct_function_export_name(
|
||||
s.as_ref(),
|
||||
self.name.as_ref(),
|
||||
);
|
||||
syn::LitStr::new(&name, Span::def_site())
|
||||
}
|
||||
|
||||
@ -256,110 +264,83 @@ impl Function {
|
||||
syn::Ident::from(generated_name)
|
||||
}
|
||||
|
||||
pub fn shared(&self) -> shared::Function {
|
||||
shared::Function {
|
||||
name: self.name.as_ref().to_string(),
|
||||
arguments: self.arguments.iter().map(|t| t.shared()).collect(),
|
||||
ret: self.ret.as_ref().map(|t| t.shared()),
|
||||
}
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("arguments", &|a| a.list(&self.arguments, Type::wbg_literal)),
|
||||
("ret", &|a| {
|
||||
match self.ret {
|
||||
Some(ref s) => s.wbg_literal(a),
|
||||
None => a.append("null"),
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_path_ident(path: &syn::Path) -> syn::Ident {
|
||||
pub fn extract_path_ident(path: &syn::Path) -> Option<syn::Ident> {
|
||||
if path.leading_colon.is_some() {
|
||||
panic!("unsupported leading colon in path")
|
||||
return None
|
||||
}
|
||||
if path.segments.len() != 1 {
|
||||
panic!("unsupported path that needs name resolution")
|
||||
return None
|
||||
}
|
||||
match path.segments.first().unwrap().value().arguments {
|
||||
syn::PathArguments::None => {}
|
||||
_ => panic!("unsupported path that has path arguments")
|
||||
_ => return None,
|
||||
}
|
||||
path.segments.first().unwrap().value().ident
|
||||
path.segments.first().map(|v| v.value().ident)
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn from(ty: &syn::Type) -> Type {
|
||||
match *ty {
|
||||
syn::Type::Reference(ref r) => {
|
||||
if r.lifetime.is_some() {
|
||||
panic!("can't have lifetimes on references yet");
|
||||
}
|
||||
let mutable = r.mutability.is_some();
|
||||
match *r.elem {
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
||||
let ident = extract_path_ident(path);
|
||||
match ident.as_ref() {
|
||||
"str" => {
|
||||
if mutable {
|
||||
panic!("mutable strings not allowed");
|
||||
}
|
||||
Type::BorrowedStr
|
||||
}
|
||||
"JsObject" if !mutable => Type::JsObjectRef,
|
||||
"JsObject" if mutable => {
|
||||
panic!("can't have mutable js object refs")
|
||||
}
|
||||
_ if mutable => Type::ByMutRef(ident),
|
||||
_ => Type::ByRef(ident),
|
||||
match ident.as_ref().map(|s| s.as_ref()) {
|
||||
Some("str") => return Type::BorrowedStr,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => panic!("unsupported reference type"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
syn::Type::Ptr(ref p) => {
|
||||
let mutable = p.const_token.is_none();
|
||||
let ident = match *p.elem {
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
||||
extract_path_ident(path)
|
||||
}
|
||||
_ => panic!("unsupported reference type"),
|
||||
};
|
||||
if mutable {
|
||||
Type::RawMutPtr(ident)
|
||||
return if r.mutability.is_some() {
|
||||
Type::ByMutRef((*r.elem).clone())
|
||||
} else {
|
||||
Type::RawConstPtr(ident)
|
||||
Type::ByRef((*r.elem).clone())
|
||||
}
|
||||
}
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
||||
let ident = extract_path_ident(path);
|
||||
match ident.as_ref() {
|
||||
"i8" |
|
||||
"u8" |
|
||||
"u16" |
|
||||
"i16" |
|
||||
"u32" |
|
||||
"i32" |
|
||||
"isize" |
|
||||
"usize" |
|
||||
"f32" |
|
||||
"f64" => {
|
||||
Type::Integer(ident)
|
||||
}
|
||||
"bool" => Type::Boolean,
|
||||
"String" => Type::String,
|
||||
"JsObject" => Type::JsObject,
|
||||
_ => Type::ByValue(ident),
|
||||
match ident.as_ref().map(|s| s.as_ref()) {
|
||||
Some("String") => return Type::String,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => panic!("unsupported type"),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Type::ByValue(ty.clone())
|
||||
}
|
||||
|
||||
fn shared(&self) -> shared::Type {
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
match *self {
|
||||
Type::Integer(_) |
|
||||
Type::RawConstPtr(_) |
|
||||
Type::RawMutPtr(_) => shared::Type::Number,
|
||||
Type::BorrowedStr => shared::Type::BorrowedStr,
|
||||
Type::String => shared::Type::String,
|
||||
Type::ByValue(n) => shared::Type::ByValue(n.to_string()),
|
||||
Type::ByRef(n) => shared::Type::ByRef(n.to_string()),
|
||||
Type::ByMutRef(n) => shared::Type::ByMutRef(n.to_string()),
|
||||
Type::JsObject => shared::Type::JsObject,
|
||||
Type::JsObjectRef => shared::Type::JsObjectRef,
|
||||
Type::Boolean => shared::Type::Boolean,
|
||||
Type::BorrowedStr => a.char(shared::TYPE_BORROWED_STR),
|
||||
Type::String => a.char(shared::TYPE_STRING),
|
||||
Type::ByValue(ref t) => {
|
||||
a.as_char(my_quote! {
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR as u8
|
||||
});
|
||||
}
|
||||
Type::ByRef(ref ty) |
|
||||
Type::ByMutRef(ref ty) => {
|
||||
a.as_char(my_quote! {
|
||||
((<#ty as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR as u32) |
|
||||
::wasm_bindgen::convert::DESCRIPTOR_CUSTOM_REF_FLAG) as u8
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -374,7 +355,7 @@ impl Struct {
|
||||
}
|
||||
|
||||
pub fn free_function(&self) -> syn::Ident {
|
||||
syn::Ident::from(self.shared().free_function())
|
||||
syn::Ident::from(shared::free_function(self.name.as_ref()))
|
||||
}
|
||||
|
||||
pub fn push_item(&mut self, item: &syn::ImplItem) {
|
||||
@ -412,33 +393,53 @@ impl Struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shared(&self) -> shared::Struct {
|
||||
shared::Struct {
|
||||
name: self.name.to_string(),
|
||||
functions: self.functions.iter().map(|f| f.shared()).collect(),
|
||||
methods: self.methods.iter().map(|f| f.shared()).collect(),
|
||||
}
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("functions", &|a| a.list(&self.functions, Function::wbg_literal)),
|
||||
("methods", &|a| a.list(&self.methods, Method::wbg_literal)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Method {
|
||||
pub fn shared(&self) -> shared::Method {
|
||||
shared::Method {
|
||||
mutable: self.mutable,
|
||||
function: self.function.shared(),
|
||||
}
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("mutable", &|a| a.bool(self.mutable)),
|
||||
("function", &|a| self.function.wbg_literal(a)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportStruct {
|
||||
fn shared(&self) -> shared::ImportStruct {
|
||||
shared::ImportStruct {
|
||||
module: self.module.clone(),
|
||||
name: self.name.to_string(),
|
||||
functions: self.functions.iter()
|
||||
.map(|&(b, ref f)| (b, f.wasm_function.shared()))
|
||||
.collect(),
|
||||
}
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("module", &|a| {
|
||||
match self.module {
|
||||
Some(ref s) => a.str(s),
|
||||
None => a.append("null"),
|
||||
}
|
||||
}),
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("functions", &|a| {
|
||||
a.list(&self.functions,
|
||||
|&(is_method, ref f), a| {
|
||||
a.fields(&[
|
||||
("method", &|a| a.bool(is_method)),
|
||||
("function", &|a| f.wasm_function.wbg_literal(a)),
|
||||
]);
|
||||
})
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Import {
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("module", &|a| a.str(&self.module)),
|
||||
("function", &|a| self.function.wasm_function.wbg_literal(a)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -503,3 +504,91 @@ fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str)
|
||||
}
|
||||
syn::parse_error()
|
||||
}
|
||||
|
||||
struct LiteralBuilder<'a> {
|
||||
dst: &'a mut Tokens,
|
||||
cnt: usize,
|
||||
}
|
||||
|
||||
impl<'a> LiteralBuilder<'a> {
|
||||
fn byte(&mut self, byte: u8) {
|
||||
if self.cnt > 0 {
|
||||
::syn::token::Comma::default().to_tokens(self.dst);
|
||||
}
|
||||
self.cnt += 1;
|
||||
byte.to_tokens(self.dst);
|
||||
}
|
||||
|
||||
fn append(&mut self, s: &str) {
|
||||
for byte in s.bytes() {
|
||||
self.byte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
fn str(&mut self, s: &str) {
|
||||
self.append("\"");
|
||||
self.append(s);
|
||||
self.append("\"");
|
||||
}
|
||||
|
||||
fn bool(&mut self, v: bool) {
|
||||
if v {
|
||||
self.append("true")
|
||||
} else {
|
||||
self.append("false")
|
||||
}
|
||||
}
|
||||
|
||||
fn char(&mut self, s: char) {
|
||||
self.append("\"\\u");
|
||||
let s = s as u32;
|
||||
self.byte(to_hex((s >> 12) as u8));
|
||||
self.byte(to_hex((s >> 8) as u8));
|
||||
self.byte(to_hex((s >> 4) as u8));
|
||||
self.byte(to_hex((s >> 0) as u8));
|
||||
self.append("\"");
|
||||
|
||||
fn to_hex(a: u8) -> u8 {
|
||||
let a = a & 0xf;
|
||||
match a {
|
||||
0 ... 9 => b'0' + a,
|
||||
_ => b'a'+ a - 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_char(&mut self, tokens: Tokens) {
|
||||
self.append("\"");
|
||||
::syn::token::Comma::default().to_tokens(self.dst);
|
||||
tokens.to_tokens(self.dst);
|
||||
self.cnt += 1;
|
||||
self.append("\"");
|
||||
}
|
||||
|
||||
fn fields(&mut self, fields: &[(&str, &Fn(&mut Self))]) {
|
||||
self.append("{");
|
||||
for (i, &(field, cb)) in fields.iter().enumerate() {
|
||||
if i > 0 {
|
||||
self.append(",");
|
||||
}
|
||||
self.str(field);
|
||||
self.append(":");
|
||||
cb(self);
|
||||
}
|
||||
self.append("}");
|
||||
}
|
||||
|
||||
fn list<T, F>(&mut self, list: T, mut cb: F)
|
||||
where F: FnMut(T::Item, &mut Self),
|
||||
T: IntoIterator,
|
||||
{
|
||||
self.append("[");
|
||||
for (i, element) in list.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
self.append(",");
|
||||
}
|
||||
cb(element, self);
|
||||
}
|
||||
self.append("]");
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![recursion_limit = "128"]
|
||||
#![feature(proc_macro)]
|
||||
|
||||
#[macro_use]
|
||||
@ -7,23 +8,24 @@ extern crate quote;
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
extern crate serde_json;
|
||||
extern crate wasm_bindgen_shared;
|
||||
extern crate wasm_bindgen_shared as shared;
|
||||
|
||||
use std::char;
|
||||
use std::sync::atomic::*;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenNode, Delimiter, TokenTree};
|
||||
use quote::{Tokens, ToTokens};
|
||||
|
||||
macro_rules! my_quote {
|
||||
($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*))
|
||||
}
|
||||
|
||||
mod ast;
|
||||
|
||||
static MALLOC_GENERATED: AtomicBool = ATOMIC_BOOL_INIT;
|
||||
static BOXED_STR_GENERATED: AtomicBool = ATOMIC_BOOL_INIT;
|
||||
|
||||
macro_rules! my_quote {
|
||||
($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
// Parse the input as a list of Rust items, reusing the `syn::File` parser.
|
||||
@ -80,8 +82,8 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
for function in program.free_functions.iter() {
|
||||
bindgen_fn(function, &mut ret);
|
||||
}
|
||||
for s in program.structs.iter() {
|
||||
bindgen_struct(s, &mut ret);
|
||||
for (i, s) in program.structs.iter().enumerate() {
|
||||
bindgen_struct(i, s, &mut ret);
|
||||
}
|
||||
for i in program.imports.iter() {
|
||||
bindgen_import(i, &mut ret);
|
||||
@ -98,19 +100,14 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
let generated_static_name = format!("__WASM_BINDGEN_GENERATED{}",
|
||||
CNT.fetch_add(1, Ordering::SeqCst));
|
||||
let generated_static_name = syn::Ident::from(generated_static_name);
|
||||
let mut generated_static = String::from("wbg:");
|
||||
generated_static.push_str(&serde_json::to_string(&program.shared()).unwrap());
|
||||
let generated_static_value = syn::LitByteStr::new(
|
||||
generated_static.as_bytes(),
|
||||
Span::def_site(),
|
||||
);
|
||||
let generated_static_length = generated_static.len();
|
||||
let mut generated_static_value = Tokens::new();
|
||||
let generated_static_length = program.wbg_literal(&mut generated_static_value);
|
||||
|
||||
(my_quote! {
|
||||
#[no_mangle]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static #generated_static_name: [u8; #generated_static_length] =
|
||||
*#generated_static_value;
|
||||
[#generated_static_value];
|
||||
}).to_tokens(&mut ret);
|
||||
|
||||
// println!("{}", ret);
|
||||
@ -127,7 +124,7 @@ fn bindgen_fn(function: &ast::Function, into: &mut Tokens) {
|
||||
into)
|
||||
}
|
||||
|
||||
fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
|
||||
fn bindgen_struct(idx: usize, s: &ast::Struct, into: &mut Tokens) {
|
||||
for f in s.functions.iter() {
|
||||
bindgen_struct_fn(s, f, into);
|
||||
}
|
||||
@ -137,11 +134,47 @@ fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
|
||||
|
||||
let name = &s.name;
|
||||
let free_fn = s.free_function();
|
||||
let c = char::from_u32(idx as u32 * 2 + shared::TYPE_CUSTOM_START);
|
||||
(my_quote! {
|
||||
impl ::wasm_bindgen::convert::WasmBoundary for #name {
|
||||
type Js = u32;
|
||||
const DESCRIPTOR: char = #c;
|
||||
|
||||
fn into_js(self) -> u32 {
|
||||
Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32
|
||||
}
|
||||
|
||||
unsafe fn from_js(js: u32) -> Self {
|
||||
let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>;
|
||||
::wasm_bindgen::__rt::assert_not_null(js);
|
||||
let js = Box::from_raw(js);
|
||||
js.borrow_mut(); // make sure no one's borrowing
|
||||
js.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name {
|
||||
type RefAnchor = ::wasm_bindgen::__rt::Ref<'static, #name>;
|
||||
unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor {
|
||||
let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>;
|
||||
::wasm_bindgen::__rt::assert_not_null(js);
|
||||
(*js).borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::wasm_bindgen::convert::FromRefMutWasmBoundary for #name {
|
||||
type RefAnchor = ::wasm_bindgen::__rt::RefMut<'static, #name>;
|
||||
|
||||
unsafe fn from_js_ref_mut(js: Self::Js) -> Self::RefAnchor {
|
||||
let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>;
|
||||
::wasm_bindgen::__rt::assert_not_null(js);
|
||||
(*js).borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern fn #free_fn(ptr: *mut ::wasm_bindgen::__rt::WasmRefCell<#name>) {
|
||||
::wasm_bindgen::__rt::assert_not_null(ptr);
|
||||
drop(Box::from_raw(ptr));
|
||||
pub unsafe extern fn #free_fn(ptr: u32) {
|
||||
<#name as ::wasm_bindgen::convert::WasmBoundary>::from_js(ptr);
|
||||
}
|
||||
}).to_tokens(into);
|
||||
}
|
||||
@ -198,21 +231,6 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
let i = i + offset;
|
||||
let ident = syn::Ident::from(format!("arg{}", i));
|
||||
match *ty {
|
||||
ast::Type::Integer(i) => {
|
||||
args.push(my_quote! { #ident: #i });
|
||||
}
|
||||
ast::Type::Boolean => {
|
||||
args.push(my_quote! { #ident: u32 });
|
||||
arg_conversions.push(my_quote! {
|
||||
let #ident = #ident != 0;
|
||||
});
|
||||
}
|
||||
ast::Type::RawMutPtr(i) => {
|
||||
args.push(my_quote! { #ident: *mut #i });
|
||||
}
|
||||
ast::Type::RawConstPtr(i) => {
|
||||
args.push(my_quote! { #ident: *const #i });
|
||||
}
|
||||
ast::Type::BorrowedStr => {
|
||||
malloc = malloc || !MALLOC_GENERATED.swap(true, Ordering::SeqCst);
|
||||
let ptr = syn::Ident::from(format!("arg{}_ptr", i));
|
||||
@ -239,90 +257,63 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
};
|
||||
});
|
||||
}
|
||||
ast::Type::ByValue(name) => {
|
||||
args.push(my_quote! { #ident: *mut ::wasm_bindgen::__rt::WasmRefCell<#name> });
|
||||
ast::Type::ByValue(ref t) => {
|
||||
args.push(my_quote! {
|
||||
#ident: <#t as ::wasm_bindgen::convert::WasmBoundary >::Js
|
||||
});
|
||||
arg_conversions.push(my_quote! {
|
||||
::wasm_bindgen::__rt::assert_not_null(#ident);
|
||||
let #ident = unsafe {
|
||||
(*#ident).borrow_mut();
|
||||
Box::from_raw(#ident).into_inner()
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>
|
||||
::from_js(#ident)
|
||||
};
|
||||
});
|
||||
}
|
||||
ast::Type::ByRef(name) => {
|
||||
args.push(my_quote! { #ident: *mut ::wasm_bindgen::__rt::WasmRefCell<#name> });
|
||||
ast::Type::ByRef(ref ty) => {
|
||||
args.push(my_quote! {
|
||||
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
});
|
||||
arg_conversions.push(my_quote! {
|
||||
::wasm_bindgen::__rt::assert_not_null(#ident);
|
||||
let #ident = unsafe { (*#ident).borrow() };
|
||||
let #ident = unsafe {
|
||||
<#ty as ::wasm_bindgen::convert::FromRefWasmBoundary>
|
||||
::from_js_ref(#ident)
|
||||
};
|
||||
let #ident = &*#ident;
|
||||
});
|
||||
}
|
||||
ast::Type::ByMutRef(name) => {
|
||||
args.push(my_quote! { #ident: *mut ::wasm_bindgen::__rt::WasmRefCell<#name> });
|
||||
ast::Type::ByMutRef(ref ty) => {
|
||||
args.push(my_quote! {
|
||||
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
});
|
||||
arg_conversions.push(my_quote! {
|
||||
::wasm_bindgen::__rt::assert_not_null(#ident);
|
||||
let mut #ident = unsafe { (*#ident).borrow_mut() };
|
||||
let mut #ident = unsafe {
|
||||
<#ty as ::wasm_bindgen::convert::FromRefMutWasmBoundary>
|
||||
::from_js_ref_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 });
|
||||
}
|
||||
let ret_ty;
|
||||
let convert_ret;
|
||||
match ret_type {
|
||||
Some(&ast::Type::Integer(i)) => {
|
||||
ret_ty = my_quote! { -> #i };
|
||||
convert_ret = my_quote! { #ret };
|
||||
}
|
||||
Some(&ast::Type::Boolean) => {
|
||||
ret_ty = my_quote! { -> u32 };
|
||||
convert_ret = my_quote! { #ret as u32 };
|
||||
}
|
||||
Some(&ast::Type::RawMutPtr(i)) => {
|
||||
ret_ty = my_quote! { -> *mut #i };
|
||||
convert_ret = my_quote! { #ret };
|
||||
}
|
||||
Some(&ast::Type::RawConstPtr(i)) => {
|
||||
ret_ty = my_quote! { -> *const #i };
|
||||
convert_ret = my_quote! { #ret };
|
||||
}
|
||||
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::ByMutRef(_)) => panic!("can't return a borrowed ref"),
|
||||
Some(&ast::Type::String) => {
|
||||
boxed_str = !BOXED_STR_GENERATED.swap(true, Ordering::SeqCst);
|
||||
ret_ty = my_quote! { -> *mut String };
|
||||
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
|
||||
}
|
||||
Some(&ast::Type::ByValue(name)) => {
|
||||
ret_ty = my_quote! { -> *mut ::wasm_bindgen::__rt::WasmRefCell<#name> };
|
||||
Some(&ast::Type::ByValue(ref t)) => {
|
||||
ret_ty = my_quote! {
|
||||
-> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
};
|
||||
convert_ret = my_quote! {
|
||||
Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(#ret)))
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret)
|
||||
};
|
||||
}
|
||||
Some(&ast::Type::JsObject) => {
|
||||
ret_ty = my_quote! { -> u32 };
|
||||
convert_ret = my_quote! {
|
||||
::wasm_bindgen::JsObject::__into_idx(#ret)
|
||||
};
|
||||
}
|
||||
Some(&ast::Type::JsObjectRef) => {
|
||||
Some(&ast::Type::BorrowedStr) |
|
||||
Some(&ast::Type::ByMutRef(_)) |
|
||||
Some(&ast::Type::ByRef(_)) => {
|
||||
panic!("can't return a borrowed ref");
|
||||
}
|
||||
None => {
|
||||
@ -331,6 +322,8 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move this function into wasm-bindgen-the-crate and then gc it out
|
||||
// if it's not used.
|
||||
let malloc = if malloc {
|
||||
my_quote! {
|
||||
#[no_mangle]
|
||||
@ -429,8 +422,10 @@ impl ToTokens for Receiver {
|
||||
}
|
||||
|
||||
fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||
let import_name = import.function.wasm_function.shared()
|
||||
.mangled_import_name(None);
|
||||
let import_name = shared::mangled_import_name(
|
||||
None,
|
||||
import.function.wasm_function.name.as_ref(),
|
||||
);
|
||||
bindgen_import_function(&import.function, &import_name, tokens);
|
||||
}
|
||||
|
||||
@ -445,8 +440,10 @@ fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) {
|
||||
let mut methods = Tokens::new();
|
||||
|
||||
for &(_is_method, ref f) in import.functions.iter() {
|
||||
let import_name = f.wasm_function.shared()
|
||||
.mangled_import_name(Some(&import.name.to_string()));
|
||||
let import_name = shared::mangled_import_name(
|
||||
Some(&import.name.to_string()),
|
||||
f.wasm_function.name.as_ref(),
|
||||
);
|
||||
bindgen_import_function(f, &import_name, &mut methods);
|
||||
}
|
||||
|
||||
@ -494,26 +491,6 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
|
||||
for (ty, name) in import.wasm_function.arguments.iter().zip(names) {
|
||||
match *ty {
|
||||
ast::Type::Integer(i) => {
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! { #name: #i });
|
||||
arg_conversions.push(my_quote! {});
|
||||
}
|
||||
ast::Type::Boolean => {
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! { #name: u32 });
|
||||
arg_conversions.push(my_quote! { let #name = #name as u32; });
|
||||
}
|
||||
ast::Type::RawMutPtr(i) => {
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! { #name: *mut #i });
|
||||
arg_conversions.push(my_quote! {});
|
||||
}
|
||||
ast::Type::RawConstPtr(i) => {
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! { #name: *const #i });
|
||||
arg_conversions.push(my_quote! {});
|
||||
}
|
||||
ast::Type::BorrowedStr => {
|
||||
let ptr = syn::Ident::from(format!("{}_ptr", name));
|
||||
let len = syn::Ident::from(format!("{}_len", name));
|
||||
@ -526,59 +503,70 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
let #len = #name.len();
|
||||
});
|
||||
}
|
||||
ast::Type::JsObject => {
|
||||
ast::Type::ByValue(ref t) => {
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! {
|
||||
#name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
});
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
|
||||
::into_js(#name);
|
||||
});
|
||||
}
|
||||
ast::Type::ByMutRef(_) => panic!("urgh mut"),
|
||||
ast::Type::ByRef(ref t) => {
|
||||
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);
|
||||
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
|
||||
::to_js_ref(#name);
|
||||
});
|
||||
}
|
||||
ast::Type::JsObjectRef => {
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! { #name: u32 });
|
||||
// TODO: need to test this
|
||||
ast::Type::String => {
|
||||
let ptr = syn::Ident::from(format!("{}_ptr", name));
|
||||
let len = syn::Ident::from(format!("{}_len", name));
|
||||
abi_argument_names.push(ptr);
|
||||
abi_argument_names.push(len);
|
||||
abi_arguments.push(my_quote! { #ptr: *const u8 });
|
||||
abi_arguments.push(my_quote! { #len: usize });
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = ::wasm_bindgen::JsObject::__get_idx(#name);
|
||||
let #ptr = #name.as_ptr();
|
||||
let #len = #name.len();
|
||||
::std::mem::forget(#name);
|
||||
});
|
||||
}
|
||||
ast::Type::String => panic!("can't use `String` in foreign functions"),
|
||||
ast::Type::ByValue(_name) |
|
||||
ast::Type::ByRef(_name) |
|
||||
ast::Type::ByMutRef(_name) => {
|
||||
panic!("can't use struct types in foreign functions yet");
|
||||
}
|
||||
}
|
||||
}
|
||||
let abi_ret;
|
||||
let convert_ret;
|
||||
match import.wasm_function.ret {
|
||||
Some(ast::Type::Integer(i)) => {
|
||||
abi_ret = my_quote! { #i };
|
||||
convert_ret = my_quote! { #ret_ident };
|
||||
}
|
||||
Some(ast::Type::Boolean) => {
|
||||
abi_ret = my_quote! { u32 };
|
||||
convert_ret = my_quote! { #ret_ident != 0 };
|
||||
}
|
||||
Some(ast::Type::RawConstPtr(i)) => {
|
||||
abi_ret = my_quote! { *const #i };
|
||||
convert_ret = my_quote! { #ret_ident };
|
||||
}
|
||||
Some(ast::Type::RawMutPtr(i)) => {
|
||||
abi_ret = my_quote! { *mut #i };
|
||||
convert_ret = my_quote! { #ret_ident };
|
||||
}
|
||||
Some(ast::Type::JsObject) => {
|
||||
abi_ret = my_quote! { u32 };
|
||||
Some(ast::Type::ByValue(ref t)) => {
|
||||
abi_ret = my_quote! {
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
};
|
||||
convert_ret = my_quote! {
|
||||
::wasm_bindgen::JsObject::__from_idx(#ret_ident)
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::from_js(#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::ByRef(_)) => panic!("can't return a borrowed ref"),
|
||||
|
||||
// TODO: add a test for this
|
||||
Some(ast::Type::String) => {
|
||||
let name = syn::Ident::from("__ret_strlen");
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! { #name: *mut usize });
|
||||
arg_conversions.push(my_quote! {
|
||||
let mut #name = 0;
|
||||
});
|
||||
abi_ret = my_quote! { *const u8 };
|
||||
convert_ret = my_quote! {
|
||||
let __v = Vec::from_raw_parts(#ret_ident, #name, #name);
|
||||
String::from_utf8_unchecked(__v)
|
||||
};
|
||||
}
|
||||
Some(ast::Type::BorrowedStr) |
|
||||
Some(ast::Type::ByRef(_)) |
|
||||
Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
|
||||
Some(ast::Type::String) => panic!("can't return a string in foreign functions"),
|
||||
Some(ast::Type::ByValue(_)) => panic!("can't return a struct in a foreign function"),
|
||||
None => {
|
||||
abi_ret = my_quote! { () };
|
||||
convert_ret = my_quote! {};
|
||||
|
@ -5,8 +5,9 @@ extern crate serde_derive;
|
||||
pub struct Program {
|
||||
pub structs: Vec<Struct>,
|
||||
pub free_functions: Vec<Function>,
|
||||
pub imports: Vec<(String, Function)>,
|
||||
pub imports: Vec<Import>,
|
||||
pub imported_structs: Vec<ImportStruct>,
|
||||
pub custom_type_names: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -16,11 +17,23 @@ pub struct Struct {
|
||||
pub methods: Vec<Method>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Import {
|
||||
pub module: String,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ImportStruct {
|
||||
pub module: Option<String>,
|
||||
pub name: String,
|
||||
pub functions: Vec<(bool, Function)>,
|
||||
pub functions: Vec<ImportStructFunction>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ImportStructFunction {
|
||||
pub method: bool,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -36,58 +49,66 @@ pub struct Function {
|
||||
pub ret: Option<Type>,
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
pub fn free_function(&self) -> String {
|
||||
let mut name = format!("__wbindgen_");
|
||||
name.extend(self.name
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase()));
|
||||
name.push_str("_free");
|
||||
return name
|
||||
pub fn free_function(struct_name: &str) -> String {
|
||||
let mut name = format!("__wbindgen_");
|
||||
name.extend(struct_name
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase()));
|
||||
name.push_str("_free");
|
||||
return name
|
||||
}
|
||||
|
||||
pub fn free_function_export_name(function_name: &str) -> String {
|
||||
function_name.to_string()
|
||||
}
|
||||
|
||||
pub fn struct_function_export_name(struct_: &str, f: &str) -> String {
|
||||
let mut name = struct_
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase())
|
||||
.collect::<String>();
|
||||
name.push_str("_");
|
||||
name.push_str(f);
|
||||
return name
|
||||
}
|
||||
|
||||
pub fn mangled_import_name(struct_: Option<&str>, f: &str) -> String {
|
||||
match struct_ {
|
||||
Some(s) => format!("__wbg_s_{}_{}", s, f),
|
||||
None => format!("__wbg_f_{}", f),
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn free_function_export_name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
pub type Type = char;
|
||||
|
||||
pub fn struct_function_export_name(&self, struct_: &str) -> String {
|
||||
let mut name = struct_
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase())
|
||||
.collect::<String>();
|
||||
name.push_str("_");
|
||||
name.push_str(&self.name);
|
||||
return name
|
||||
}
|
||||
pub const TYPE_NUMBER: char = '\u{5e}';
|
||||
pub const TYPE_BORROWED_STR: char = '\u{5f}';
|
||||
pub const TYPE_STRING: char = '\u{60}';
|
||||
pub const TYPE_BOOLEAN: char = '\u{61}';
|
||||
pub const TYPE_JS_OWNED: char = '\u{62}';
|
||||
pub const TYPE_JS_REF: char = '\u{63}';
|
||||
|
||||
pub fn mangled_import_name(&self, struct_: Option<&str>) -> String {
|
||||
match struct_ {
|
||||
Some(s) => format!("__wbg_s_{}_{}", s, self.name),
|
||||
None => format!("__wbg_f_{}", self.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub const TYPE_CUSTOM_START: u32 = 0x64;
|
||||
pub const TYPE_CUSTOM_REF_FLAG: u32 = 1;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Type {
|
||||
Number,
|
||||
BorrowedStr,
|
||||
String,
|
||||
ByValue(String),
|
||||
ByRef(String),
|
||||
ByMutRef(String),
|
||||
JsObject,
|
||||
JsObjectRef,
|
||||
Boolean,
|
||||
}
|
||||
// #[derive(Serialize, Deserialize)]
|
||||
// pub enum Type {
|
||||
// Number,
|
||||
// BorrowedStr,
|
||||
// String,
|
||||
// ByValue(String), // wrapper class
|
||||
// ByRef(String), // wrapper class
|
||||
// ByMutRef(String), // wrapper class
|
||||
// JsObject,
|
||||
// JsObjectRef,
|
||||
// Boolean,
|
||||
// }
|
||||
|
||||
impl Type {
|
||||
pub fn is_number(&self) -> bool {
|
||||
match *self {
|
||||
Type::Number => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl Type {
|
||||
// pub fn is_number(&self) -> bool {
|
||||
// match *self {
|
||||
// Type::Number => true,
|
||||
// _ => false,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
122
src/convert.rs
Normal file
122
src/convert.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::JsObject;
|
||||
|
||||
// keep in sync with shared/src/lib.rs TYPE constants
|
||||
pub const DESCRIPTOR_CUSTOM_REF_FLAG: u32 = 0x1;
|
||||
pub const DESCRIPTOR_NUMBER: char = '\u{5e}';
|
||||
pub const DESCRIPTOR_BOOLEAN: char = '\u{61}';
|
||||
pub const DESCRIPTOR_JS_OWNED: char = '\u{62}';
|
||||
|
||||
pub trait WasmBoundary {
|
||||
type Js: WasmAbi;
|
||||
const DESCRIPTOR: char;
|
||||
|
||||
fn into_js(self) -> Self::Js;
|
||||
unsafe fn from_js(js: Self::Js) -> Self;
|
||||
}
|
||||
|
||||
pub trait FromRefWasmBoundary: WasmBoundary {
|
||||
type RefAnchor: Deref<Target = Self>;
|
||||
|
||||
unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor;
|
||||
}
|
||||
|
||||
pub trait FromRefMutWasmBoundary: WasmBoundary {
|
||||
type RefAnchor: DerefMut<Target = Self>;
|
||||
|
||||
unsafe fn from_js_ref_mut(js: Self::Js) -> Self::RefAnchor;
|
||||
}
|
||||
|
||||
pub trait ToRefWasmBoundary: WasmBoundary {
|
||||
fn to_js_ref(&self) -> u32;
|
||||
}
|
||||
|
||||
pub unsafe trait WasmAbi {}
|
||||
|
||||
unsafe impl WasmAbi for u32 {}
|
||||
unsafe impl WasmAbi for u64 {}
|
||||
unsafe impl WasmAbi for f32 {}
|
||||
unsafe impl WasmAbi for f64 {}
|
||||
|
||||
macro_rules! simple {
|
||||
($($t:tt)*) => ($(
|
||||
impl WasmBoundary for $t {
|
||||
type Js = $t;
|
||||
const DESCRIPTOR: char = DESCRIPTOR_NUMBER;
|
||||
|
||||
fn into_js(self) -> $t { self }
|
||||
unsafe fn from_js(js: $t) -> $t { js }
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
simple!(u32 u64 f32 f64);
|
||||
|
||||
macro_rules! as_u32 {
|
||||
($($t:tt)*) => ($(
|
||||
impl WasmBoundary for $t {
|
||||
type Js = u32;
|
||||
const DESCRIPTOR: char = DESCRIPTOR_NUMBER;
|
||||
|
||||
fn into_js(self) -> u32 { self as u32 }
|
||||
unsafe fn from_js(js: u32) -> $t { js as $t }
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
as_u32!(i8 u8 i16 u16 i32 isize usize);
|
||||
|
||||
impl WasmBoundary for bool {
|
||||
type Js = u32;
|
||||
const DESCRIPTOR: char = DESCRIPTOR_BOOLEAN;
|
||||
|
||||
fn into_js(self) -> u32 { self as u32 }
|
||||
unsafe fn from_js(js: u32) -> bool { js != 0 }
|
||||
}
|
||||
|
||||
impl<T> WasmBoundary for *const T {
|
||||
type Js = u32;
|
||||
const DESCRIPTOR: char = DESCRIPTOR_NUMBER;
|
||||
|
||||
fn into_js(self) -> u32 { self as u32 }
|
||||
unsafe fn from_js(js: u32) -> *const T { js as *const T }
|
||||
}
|
||||
|
||||
impl<T> WasmBoundary for *mut T {
|
||||
type Js = u32;
|
||||
const DESCRIPTOR: char = DESCRIPTOR_NUMBER;
|
||||
|
||||
fn into_js(self) -> u32 { self as u32 }
|
||||
unsafe fn from_js(js: u32) -> *mut T { js as *mut T }
|
||||
}
|
||||
|
||||
impl WasmBoundary for JsObject {
|
||||
type Js = u32;
|
||||
const DESCRIPTOR: char = DESCRIPTOR_JS_OWNED;
|
||||
|
||||
fn into_js(self) -> u32 {
|
||||
let ret = self.idx;
|
||||
mem::forget(self);
|
||||
return ret
|
||||
}
|
||||
|
||||
unsafe fn from_js(js: u32) -> JsObject {
|
||||
JsObject { idx: js }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToRefWasmBoundary for JsObject {
|
||||
fn to_js_ref(&self) -> u32 {
|
||||
self.idx
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRefWasmBoundary for JsObject {
|
||||
type RefAnchor = ManuallyDrop<JsObject>;
|
||||
|
||||
unsafe fn from_js_ref(js: u32) -> ManuallyDrop<JsObject> {
|
||||
ManuallyDrop::new(JsObject { idx: js })
|
||||
}
|
||||
}
|
47
src/lib.rs
47
src/lib.rs
@ -8,7 +8,6 @@
|
||||
|
||||
extern crate wasm_bindgen_macro;
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
/// A module which is typically glob imported from:
|
||||
@ -21,6 +20,8 @@ pub mod prelude {
|
||||
pub use JsObject;
|
||||
}
|
||||
|
||||
pub mod convert;
|
||||
|
||||
/// Representation of an object owned by JS.
|
||||
///
|
||||
/// A `JsObject` doesn't actually live in Rust right now but actually in a table
|
||||
@ -38,7 +39,7 @@ impl JsObject {
|
||||
/// be owned by the JS garbage collector.
|
||||
pub fn from_str(s: &str) -> JsObject {
|
||||
unsafe {
|
||||
JsObject::__from_idx(__wbindgen_string_new(s.as_ptr(), s.len()))
|
||||
JsObject { idx: __wbindgen_string_new(s.as_ptr(), s.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +49,7 @@ impl JsObject {
|
||||
/// allocated number) and returns a handle to the JS version of it.
|
||||
pub fn from_f64(n: f64) -> JsObject {
|
||||
unsafe {
|
||||
JsObject::__from_idx(__wbindgen_number_new(n))
|
||||
JsObject { idx: __wbindgen_number_new(n) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,21 +59,21 @@ impl JsObject {
|
||||
/// allocated boolean) and returns a handle to the JS version of it.
|
||||
pub fn from_bool(b: bool) -> JsObject {
|
||||
unsafe {
|
||||
JsObject::__from_idx(__wbindgen_boolean_new(b as u32))
|
||||
JsObject { idx: __wbindgen_boolean_new(b as u32) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new JS value representing `undefined`.
|
||||
pub fn undefined() -> JsObject {
|
||||
unsafe {
|
||||
JsObject::__from_idx(__wbindgen_undefined_new())
|
||||
JsObject { idx: __wbindgen_undefined_new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new JS value representing `null`.
|
||||
pub fn null() -> JsObject {
|
||||
unsafe {
|
||||
JsObject::__from_idx(__wbindgen_null_new())
|
||||
JsObject { idx: __wbindgen_null_new() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,26 +85,26 @@ impl JsObject {
|
||||
unsafe {
|
||||
let ptr = description.map(|s| s.as_ptr()).unwrap_or(ptr::null());
|
||||
let len = description.map(|s| s.len()).unwrap_or(0);
|
||||
JsObject::__from_idx(__wbindgen_symbol_new(ptr, len))
|
||||
JsObject { idx: __wbindgen_symbol_new(ptr, len) }
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
// #[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
|
||||
// }
|
||||
|
||||
/// Returns the `f64` value of this JS value if it's an instance of a
|
||||
/// number.
|
||||
|
@ -1,125 +1,125 @@
|
||||
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 struct Math {
|
||||
fn random() -> f64;
|
||||
fn log(a: f64) -> f64;
|
||||
}
|
||||
|
||||
pub fn get_random() -> f64 {
|
||||
Math::random()
|
||||
}
|
||||
|
||||
pub fn do_log(a: f64) -> f64 {
|
||||
Math::log(a)
|
||||
}
|
||||
}
|
||||
"#)
|
||||
.file("test.ts", r#"
|
||||
import * as wasm from "./out";
|
||||
import * as assert from "assert";
|
||||
|
||||
export function test() {
|
||||
wasm.get_random();
|
||||
assert.strictEqual(wasm.do_log(1.0), Math.log(1.0));
|
||||
}
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_class() {
|
||||
test_support::project()
|
||||
.file("src/lib.rs", r#"
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
wasm_bindgen! {
|
||||
#[wasm_module = "./test"]
|
||||
extern struct Foo {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
pub fn bar() {
|
||||
Foo::bar();
|
||||
}
|
||||
}
|
||||
"#)
|
||||
.file("test.ts", r#"
|
||||
import * as wasm from "./out";
|
||||
import * as assert from "assert";
|
||||
|
||||
let called = false;
|
||||
|
||||
export class Foo {
|
||||
static bar() {
|
||||
called = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function test() {
|
||||
wasm.bar();
|
||||
assert.strictEqual(called, true);
|
||||
}
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct() {
|
||||
test_support::project()
|
||||
.file("src/lib.rs", r#"
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
wasm_bindgen! {
|
||||
#[wasm_module = "./test"]
|
||||
extern struct Foo {
|
||||
fn create() -> Foo;
|
||||
fn doit(&self);
|
||||
}
|
||||
|
||||
pub fn bar() {
|
||||
let foo = Foo::bar();
|
||||
}
|
||||
}
|
||||
"#)
|
||||
.file("test.ts", r#"
|
||||
import * as wasm from "./out";
|
||||
import * as assert from "assert";
|
||||
|
||||
let called = false;
|
||||
|
||||
export class Foo {
|
||||
static create() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
doit() {
|
||||
called = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function test() {
|
||||
wasm.bar();
|
||||
assert.strictEqual(called, true);
|
||||
}
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
// #[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 struct Math {
|
||||
// fn random() -> f64;
|
||||
// fn log(a: f64) -> f64;
|
||||
// }
|
||||
//
|
||||
// pub fn get_random() -> f64 {
|
||||
// Math::random()
|
||||
// }
|
||||
//
|
||||
// pub fn do_log(a: f64) -> f64 {
|
||||
// Math::log(a)
|
||||
// }
|
||||
// }
|
||||
// "#)
|
||||
// .file("test.ts", r#"
|
||||
// import * as wasm from "./out";
|
||||
// import * as assert from "assert";
|
||||
//
|
||||
// export function test() {
|
||||
// wasm.get_random();
|
||||
// assert.strictEqual(wasm.do_log(1.0), Math.log(1.0));
|
||||
// }
|
||||
// "#)
|
||||
// .test();
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn import_class() {
|
||||
// test_support::project()
|
||||
// .file("src/lib.rs", r#"
|
||||
// #![feature(proc_macro)]
|
||||
//
|
||||
// extern crate wasm_bindgen;
|
||||
//
|
||||
// use wasm_bindgen::prelude::*;
|
||||
//
|
||||
// wasm_bindgen! {
|
||||
// #[wasm_module = "./test"]
|
||||
// extern struct Foo {
|
||||
// fn bar();
|
||||
// }
|
||||
//
|
||||
// pub fn bar() {
|
||||
// Foo::bar();
|
||||
// }
|
||||
// }
|
||||
// "#)
|
||||
// .file("test.ts", r#"
|
||||
// import * as wasm from "./out";
|
||||
// import * as assert from "assert";
|
||||
//
|
||||
// let called = false;
|
||||
//
|
||||
// export class Foo {
|
||||
// static bar() {
|
||||
// called = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// export function test() {
|
||||
// wasm.bar();
|
||||
// assert.strictEqual(called, true);
|
||||
// }
|
||||
// "#)
|
||||
// .test();
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn construct() {
|
||||
// test_support::project()
|
||||
// .file("src/lib.rs", r#"
|
||||
// #![feature(proc_macro)]
|
||||
//
|
||||
// extern crate wasm_bindgen;
|
||||
//
|
||||
// use wasm_bindgen::prelude::*;
|
||||
//
|
||||
// wasm_bindgen! {
|
||||
// #[wasm_module = "./test"]
|
||||
// extern struct Foo {
|
||||
// fn create() -> Foo;
|
||||
// fn doit(&self);
|
||||
// }
|
||||
//
|
||||
// pub fn bar() {
|
||||
// let foo = Foo::bar();
|
||||
// }
|
||||
// }
|
||||
// "#)
|
||||
// .file("test.ts", r#"
|
||||
// import * as wasm from "./out";
|
||||
// import * as assert from "assert";
|
||||
//
|
||||
// let called = false;
|
||||
//
|
||||
// export class Foo {
|
||||
// static create() {
|
||||
// return new Foo();
|
||||
// }
|
||||
//
|
||||
// doit() {
|
||||
// called = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// export function test() {
|
||||
// wasm.bar();
|
||||
// assert.strictEqual(called, true);
|
||||
// }
|
||||
// "#)
|
||||
// .test();
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user