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:
Alex Crichton 2018-02-06 07:56:14 -08:00
parent 0320bc0d7c
commit d5ff725913
8 changed files with 772 additions and 534 deletions

View File

@ -21,7 +21,7 @@ impl<'a> Js<'a> {
self.generate_free_function(f); self.generate_free_function(f);
} }
for f in self.program.imports.iter() { 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() { for s in self.program.structs.iter() {
self.generate_struct(s); self.generate_struct(s);
@ -238,14 +238,14 @@ impl<'a> Js<'a> {
this.ptr = 0; this.ptr = 0;
wasm.{}(ptr); wasm.{}(ptr);
}} }}
", s.free_function())); ", shared::free_function(&s.name)));
ts_dst.push_str("free(): void;\n"); ts_dst.push_str("free(): void;\n");
for function in s.functions.iter() { for function in s.functions.iter() {
let (js, ts) = self.generate_function( let (js, ts) = self.generate_function(
"static", "static",
&function.name, &function.name,
&function.struct_function_export_name(&s.name), &shared::struct_function_export_name(&s.name, &function.name),
false, false,
&function.arguments, &function.arguments,
function.ret.as_ref(), function.ret.as_ref(),
@ -259,7 +259,7 @@ impl<'a> Js<'a> {
let (js, ts) = self.generate_function( let (js, ts) = self.generate_function(
"", "",
&method.function.name, &method.function.name,
&method.function.struct_function_export_name(&s.name), &shared::struct_function_export_name(&s.name, &method.function.name),
true, true,
&method.function.arguments, &method.function.arguments,
method.function.ret.as_ref(), method.function.ret.as_ref(),
@ -311,7 +311,7 @@ impl<'a> Js<'a> {
passed_args.push_str(arg); passed_args.push_str(arg);
}; };
match *arg { match *arg {
shared::Type::Number => { shared::TYPE_NUMBER => {
dst_ts.push_str(": number"); dst_ts.push_str(": number");
if self.config.debug { if self.config.debug {
self.expose_assert_num(); self.expose_assert_num();
@ -319,7 +319,7 @@ impl<'a> Js<'a> {
} }
pass(&name) pass(&name)
} }
shared::Type::Boolean => { shared::TYPE_BOOLEAN => {
dst_ts.push_str(": boolean"); dst_ts.push_str(": boolean");
if self.config.debug { if self.config.debug {
self.expose_assert_bool(); self.expose_assert_bool();
@ -330,8 +330,8 @@ impl<'a> Js<'a> {
} }
pass(&format!("arg{i} ? 1 : 0", i = i)) pass(&format!("arg{i} ? 1 : 0", i = i))
} }
shared::Type::BorrowedStr | shared::TYPE_BORROWED_STR |
shared::Type::String => { shared::TYPE_STRING => {
dst_ts.push_str(": string"); dst_ts.push_str(": string");
self.expose_pass_string_to_wasm(); self.expose_pass_string_to_wasm();
arg_conversions.push_str(&format!("\ arg_conversions.push_str(&format!("\
@ -339,14 +339,33 @@ impl<'a> Js<'a> {
", i = i, arg = name)); ", i = i, arg = name));
pass(&format!("ptr{}", i)); pass(&format!("ptr{}", i));
pass(&format!("len{}", i)); pass(&format!("len{}", i));
if let shared::Type::BorrowedStr = *arg { if *arg == shared::TYPE_BORROWED_STR {
destructors.push_str(&format!("\n\ destructors.push_str(&format!("\n\
wasm.__wbindgen_free(ptr{i}, len{i});\n\ wasm.__wbindgen_free(ptr{i}, len{i});\n\
", i = i)); ", i = i));
} }
} }
shared::Type::ByRef(ref s) | shared::TYPE_JS_OWNED => {
shared::Type::ByMutRef(ref s) => { 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)); dst_ts.push_str(&format!(": {}", s));
if self.config.debug { if self.config.debug {
self.expose_assert_class(); self.expose_assert_class();
@ -356,7 +375,9 @@ impl<'a> Js<'a> {
} }
pass(&format!("{}.ptr", name)); 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)); dst_ts.push_str(&format!(": {}", s));
if self.config.debug { if self.config.debug {
self.expose_assert_class(); self.expose_assert_class();
@ -370,23 +391,6 @@ impl<'a> Js<'a> {
", i = i, arg = name)); ", i = i, arg = name));
pass(&format!("ptr{}", i)); 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(")"); dst.push_str(")");
@ -396,24 +400,36 @@ impl<'a> Js<'a> {
dst_ts.push_str(": void"); dst_ts.push_str(": void");
format!("return ret;") format!("return ret;")
} }
Some(&shared::Type::Number) => { Some(&shared::TYPE_NUMBER) => {
dst_ts.push_str(": number"); dst_ts.push_str(": number");
format!("return ret;") format!("return ret;")
} }
Some(&shared::Type::Boolean) => { Some(&shared::TYPE_BOOLEAN) => {
dst_ts.push_str(": boolean"); dst_ts.push_str(": boolean");
format!("return ret != 0;") format!("return ret != 0;")
} }
Some(&shared::Type::JsObject) => { Some(&shared::TYPE_JS_OWNED) => {
dst_ts.push_str(": any"); dst_ts.push_str(": any");
self.expose_take_object(); self.expose_take_object();
format!("return takeObject(ret);") format!("return takeObject(ret);")
} }
Some(&shared::Type::JsObjectRef) | Some(&shared::TYPE_STRING) => {
Some(&shared::Type::BorrowedStr) | dst_ts.push_str(": string");
Some(&shared::Type::ByMutRef(_)) | self.expose_get_string_from_wasm();
Some(&shared::Type::ByRef(_)) => panic!(), format!("
Some(&shared::Type::ByValue(ref name)) => { 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(": ");
dst_ts.push_str(name); dst_ts.push_str(name);
if self.config.debug { if self.config.debug {
@ -426,17 +442,6 @@ impl<'a> Js<'a> {
", name = name) ", 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_ts.push_str(";");
dst.push_str(" {\n "); dst.push_str(" {\n ");
@ -476,7 +481,7 @@ impl<'a> Js<'a> {
import {{ {} as {} }} from '{}'; import {{ {} as {} }} from '{}';
", import.name, imported_name, module)); ", 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, &imported_name,
import) import)
} }
@ -488,10 +493,10 @@ impl<'a> Js<'a> {
", import.name, module)); ", import.name, module));
} }
for &(method, ref function) in import.functions.iter() { for import in import.functions.iter() {
self.generate_import_struct_function(&import.name, self.generate_import_struct_function(&import.function.name,
method, import.method,
function); &import.function);
} }
} }
@ -506,7 +511,7 @@ impl<'a> Js<'a> {
} else { } else {
format!("{}.{}", class, function.name) 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, &delegate,
function) function)
} }
@ -530,33 +535,30 @@ impl<'a> Js<'a> {
dst.push_str(", "); dst.push_str(", ");
} }
match *arg { match *arg {
shared::Type::Number => { shared::TYPE_NUMBER => {
invocation.push_str(&format!("arg{}", i)); invocation.push_str(&format!("arg{}", i));
dst.push_str(&format!("arg{}", i)); dst.push_str(&format!("arg{}", i));
} }
shared::Type::Boolean => { shared::TYPE_BOOLEAN => {
invocation.push_str(&format!("arg{} != 0", i)); invocation.push_str(&format!("arg{} != 0", i));
dst.push_str(&format!("arg{}", i)); dst.push_str(&format!("arg{}", i));
} }
shared::Type::BorrowedStr => { shared::TYPE_BORROWED_STR => {
self.expose_get_string_from_wasm(); self.expose_get_string_from_wasm();
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 => { shared::TYPE_JS_OWNED => {
self.expose_take_object(); self.expose_take_object();
invocation.push_str(&format!("takeObject(arg{})", i)); invocation.push_str(&format!("takeObject(arg{})", i));
dst.push_str(&format!("arg{}", i)); dst.push_str(&format!("arg{}", i));
} }
shared::Type::JsObjectRef => { shared::TYPE_JS_REF => {
self.expose_get_object(); self.expose_get_object();
invocation.push_str(&format!("getObject(arg{})", i)); invocation.push_str(&format!("getObject(arg{})", i));
dst.push_str(&format!("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"); panic!("unsupported type in import");
} }
} }
@ -564,9 +566,9 @@ impl<'a> Js<'a> {
dst.push_str(")"); dst.push_str(")");
let invoc = format!("{}({})", shim_delegate, invocation); let invoc = format!("{}({})", shim_delegate, invocation);
let invoc = match import.ret { let invoc = match import.ret {
Some(shared::Type::Number) => invoc, Some(shared::TYPE_NUMBER) => invoc,
Some(shared::Type::Boolean) => format!("{} ? 1 : 0", invoc), Some(shared::TYPE_BOOLEAN) => format!("{} ? 1 : 0", invoc),
Some(shared::Type::JsObject) => { Some(shared::TYPE_JS_OWNED) => {
self.expose_add_heap_object(); self.expose_add_heap_object();
format!("addHeapObject({})", invoc) format!("addHeapObject({})", invoc)
} }
@ -615,11 +617,13 @@ impl<'a> Js<'a> {
// fixed upstream. // fixed upstream.
let program_import = self.program.imports let program_import = self.program.imports
.iter() .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 let struct_import = self.program.imported_structs
.iter() .iter()
.flat_map(|s| s.functions.iter().map(move |f| (s, &f.1))) .flat_map(|s| s.functions.iter().map(move |f| (s, &f.function)))
.any(|(s, f)| f.mangled_import_name(Some(&s.name)) == import.field()); .any(|(s, f)| {
shared::mangled_import_name(Some(&s.name), &f.name) == import.field()
});
if program_import || struct_import { if program_import || struct_import {
import.module_mut().truncate(0); import.module_mut().truncate(0);
import.module_mut().push_str("./"); import.module_mut().push_str("./");

View File

@ -110,6 +110,7 @@ fn extract_program(module: &mut Module) -> shared::Program {
free_functions: Vec::new(), free_functions: Vec::new(),
imports: Vec::new(), imports: Vec::new(),
imported_structs: Vec::new(), imported_structs: Vec::new(),
custom_type_names: Vec::new(),
}; };
let data = match data { let data = match data {
Some(data) => data, Some(data) => data,
@ -125,13 +126,25 @@ fn extract_program(module: &mut Module) -> shared::Program {
let json = &value[4..]; let json = &value[4..];
let p = match serde_json::from_slice(json) { let p = match serde_json::from_slice(json) {
Ok(f) => f, 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.structs.extend(structs);
ret.free_functions.extend(free_functions); ret.free_functions.extend(free_functions);
ret.imports.extend(imports); ret.imports.extend(imports);
ret.imported_structs.extend(imported_structs); 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); data.entries_mut().remove(i);
} }

View File

@ -1,6 +1,7 @@
use proc_macro2::Span; use proc_macro2::Span;
use quote::{Tokens, ToTokens};
use shared;
use syn; use syn;
use wasm_bindgen_shared as shared;
pub struct Program { pub struct Program {
pub structs: Vec<Struct>, pub structs: Vec<Struct>,
@ -35,17 +36,13 @@ pub struct ImportStruct {
} }
pub enum Type { pub enum Type {
Integer(syn::Ident), // special
BorrowedStr, BorrowedStr,
String, String,
ByValue(syn::Ident),
ByRef(syn::Ident), ByRef(syn::Type),
ByMutRef(syn::Ident), ByMutRef(syn::Type),
RawMutPtr(syn::Ident), ByValue(syn::Type),
RawConstPtr(syn::Ident),
JsObject,
JsObjectRef,
Boolean,
} }
pub struct Struct { pub struct Struct {
@ -73,8 +70,13 @@ impl Program {
if item.generics.params.len() > 0 { if item.generics.params.len() > 0 {
panic!("generic impls aren't supported"); panic!("generic impls aren't supported");
} }
let name = match Type::from(&item.self_ty) { let name = match *item.self_ty {
Type::ByValue(ident) => ident, 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"), _ => panic!("unsupported self type in impl"),
}; };
let dst = self.structs let dst = self.structs
@ -162,17 +164,20 @@ impl Program {
}); });
} }
pub fn shared(&self) -> shared::Program { pub fn wbg_literal(&self, dst: &mut Tokens) -> usize {
shared::Program { let mut a = LiteralBuilder {
structs: self.structs.iter().map(|s| s.shared()).collect(), dst,
free_functions: self.free_functions.iter().map(|s| s.shared()).collect(), cnt: 0,
imports: self.imports.iter() };
.map(|i| (i.module.clone(), i.function.wasm_function.shared())) a.append("wbg:");
.collect(), a.fields(&[
imported_structs: self.imported_structs.iter() ("structs", &|a| a.list(&self.structs, Struct::wbg_literal)),
.map(|i| i.shared()) ("free_functions", &|a| a.list(&self.free_functions, Function::wbg_literal)),
.collect(), ("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 { 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()) syn::LitStr::new(&name, Span::def_site())
} }
pub fn struct_function_export_name(&self, s: syn::Ident) -> syn::LitStr { 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()) syn::LitStr::new(&name, Span::def_site())
} }
@ -256,110 +264,83 @@ impl Function {
syn::Ident::from(generated_name) syn::Ident::from(generated_name)
} }
pub fn shared(&self) -> shared::Function { fn wbg_literal(&self, a: &mut LiteralBuilder) {
shared::Function { a.fields(&[
name: self.name.as_ref().to_string(), ("name", &|a| a.str(self.name.as_ref())),
arguments: self.arguments.iter().map(|t| t.shared()).collect(), ("arguments", &|a| a.list(&self.arguments, Type::wbg_literal)),
ret: self.ret.as_ref().map(|t| t.shared()), ("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() { if path.leading_colon.is_some() {
panic!("unsupported leading colon in path") return None
} }
if path.segments.len() != 1 { if path.segments.len() != 1 {
panic!("unsupported path that needs name resolution") return None
} }
match path.segments.first().unwrap().value().arguments { match path.segments.first().unwrap().value().arguments {
syn::PathArguments::None => {} 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 { impl Type {
pub fn from(ty: &syn::Type) -> Type { pub fn from(ty: &syn::Type) -> Type {
match *ty { match *ty {
syn::Type::Reference(ref r) => { 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 { match *r.elem {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => { syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
let ident = extract_path_ident(path); let ident = extract_path_ident(path);
match ident.as_ref() { match ident.as_ref().map(|s| s.as_ref()) {
"str" => { Some("str") => return Type::BorrowedStr,
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),
} }
} }
_ => panic!("unsupported reference type"), _ => {}
} }
} return if r.mutability.is_some() {
syn::Type::Ptr(ref p) => { Type::ByMutRef((*r.elem).clone())
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)
} else { } else {
Type::RawConstPtr(ident) Type::ByRef((*r.elem).clone())
} }
} }
syn::Type::Path(syn::TypePath { qself: None, ref path }) => { syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
let ident = extract_path_ident(path); let ident = extract_path_ident(path);
match ident.as_ref() { match ident.as_ref().map(|s| s.as_ref()) {
"i8" | Some("String") => return Type::String,
"u8" | _ => {}
"u16" |
"i16" |
"u32" |
"i32" |
"isize" |
"usize" |
"f32" |
"f64" => {
Type::Integer(ident)
}
"bool" => Type::Boolean,
"String" => Type::String,
"JsObject" => Type::JsObject,
_ => Type::ByValue(ident),
} }
} }
_ => panic!("unsupported type"), _ => {}
} }
Type::ByValue(ty.clone())
} }
fn shared(&self) -> shared::Type { fn wbg_literal(&self, a: &mut LiteralBuilder) {
match *self { match *self {
Type::Integer(_) | Type::BorrowedStr => a.char(shared::TYPE_BORROWED_STR),
Type::RawConstPtr(_) | Type::String => a.char(shared::TYPE_STRING),
Type::RawMutPtr(_) => shared::Type::Number, Type::ByValue(ref t) => {
Type::BorrowedStr => shared::Type::BorrowedStr, a.as_char(my_quote! {
Type::String => shared::Type::String, <#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR as u8
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::ByRef(ref ty) |
Type::JsObject => shared::Type::JsObject, Type::ByMutRef(ref ty) => {
Type::JsObjectRef => shared::Type::JsObjectRef, a.as_char(my_quote! {
Type::Boolean => shared::Type::Boolean, ((<#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 { 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) { pub fn push_item(&mut self, item: &syn::ImplItem) {
@ -412,33 +393,53 @@ impl Struct {
} }
} }
pub fn shared(&self) -> shared::Struct { fn wbg_literal(&self, a: &mut LiteralBuilder) {
shared::Struct { a.fields(&[
name: self.name.to_string(), ("name", &|a| a.str(self.name.as_ref())),
functions: self.functions.iter().map(|f| f.shared()).collect(), ("functions", &|a| a.list(&self.functions, Function::wbg_literal)),
methods: self.methods.iter().map(|f| f.shared()).collect(), ("methods", &|a| a.list(&self.methods, Method::wbg_literal)),
} ]);
} }
} }
impl Method { impl Method {
pub fn shared(&self) -> shared::Method { fn wbg_literal(&self, a: &mut LiteralBuilder) {
shared::Method { a.fields(&[
mutable: self.mutable, ("mutable", &|a| a.bool(self.mutable)),
function: self.function.shared(), ("function", &|a| self.function.wbg_literal(a)),
} ]);
} }
} }
impl ImportStruct { impl ImportStruct {
fn shared(&self) -> shared::ImportStruct { fn wbg_literal(&self, a: &mut LiteralBuilder) {
shared::ImportStruct { a.fields(&[
module: self.module.clone(), ("module", &|a| {
name: self.name.to_string(), match self.module {
functions: self.functions.iter() Some(ref s) => a.str(s),
.map(|&(b, ref f)| (b, f.wasm_function.shared())) None => a.append("null"),
.collect(), }
} }),
("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() 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("]");
}
}

View File

@ -1,3 +1,4 @@
#![recursion_limit = "128"]
#![feature(proc_macro)] #![feature(proc_macro)]
#[macro_use] #[macro_use]
@ -7,23 +8,24 @@ extern crate quote;
extern crate proc_macro; extern crate proc_macro;
extern crate proc_macro2; extern crate proc_macro2;
extern crate serde_json; extern crate serde_json;
extern crate wasm_bindgen_shared; extern crate wasm_bindgen_shared as shared;
use std::char;
use std::sync::atomic::*; use std::sync::atomic::*;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::{Span, TokenNode, Delimiter, TokenTree}; use proc_macro2::{Span, TokenNode, Delimiter, TokenTree};
use quote::{Tokens, ToTokens}; use quote::{Tokens, ToTokens};
macro_rules! my_quote {
($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*))
}
mod ast; mod ast;
static MALLOC_GENERATED: AtomicBool = ATOMIC_BOOL_INIT; static MALLOC_GENERATED: AtomicBool = ATOMIC_BOOL_INIT;
static BOXED_STR_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] #[proc_macro]
pub fn wasm_bindgen(input: TokenStream) -> TokenStream { pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
// Parse the input as a list of Rust items, reusing the `syn::File` parser. // 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() { for function in program.free_functions.iter() {
bindgen_fn(function, &mut ret); bindgen_fn(function, &mut ret);
} }
for s in program.structs.iter() { for (i, s) in program.structs.iter().enumerate() {
bindgen_struct(s, &mut ret); bindgen_struct(i, s, &mut ret);
} }
for i in program.imports.iter() { for i in program.imports.iter() {
bindgen_import(i, &mut ret); bindgen_import(i, &mut ret);
@ -98,19 +100,14 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
let generated_static_name = format!("__WASM_BINDGEN_GENERATED{}", let generated_static_name = format!("__WASM_BINDGEN_GENERATED{}",
CNT.fetch_add(1, Ordering::SeqCst)); CNT.fetch_add(1, Ordering::SeqCst));
let generated_static_name = syn::Ident::from(generated_static_name); let generated_static_name = syn::Ident::from(generated_static_name);
let mut generated_static = String::from("wbg:"); let mut generated_static_value = Tokens::new();
generated_static.push_str(&serde_json::to_string(&program.shared()).unwrap()); let generated_static_length = program.wbg_literal(&mut generated_static_value);
let generated_static_value = syn::LitByteStr::new(
generated_static.as_bytes(),
Span::def_site(),
);
let generated_static_length = generated_static.len();
(my_quote! { (my_quote! {
#[no_mangle] #[no_mangle]
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
pub static #generated_static_name: [u8; #generated_static_length] = pub static #generated_static_name: [u8; #generated_static_length] =
*#generated_static_value; [#generated_static_value];
}).to_tokens(&mut ret); }).to_tokens(&mut ret);
// println!("{}", ret); // println!("{}", ret);
@ -127,7 +124,7 @@ fn bindgen_fn(function: &ast::Function, into: &mut Tokens) {
into) 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() { for f in s.functions.iter() {
bindgen_struct_fn(s, f, into); bindgen_struct_fn(s, f, into);
} }
@ -137,11 +134,47 @@ fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
let name = &s.name; let name = &s.name;
let free_fn = s.free_function(); let free_fn = s.free_function();
let c = char::from_u32(idx as u32 * 2 + shared::TYPE_CUSTOM_START);
(my_quote! { (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] #[no_mangle]
pub unsafe extern fn #free_fn(ptr: *mut ::wasm_bindgen::__rt::WasmRefCell<#name>) { pub unsafe extern fn #free_fn(ptr: u32) {
::wasm_bindgen::__rt::assert_not_null(ptr); <#name as ::wasm_bindgen::convert::WasmBoundary>::from_js(ptr);
drop(Box::from_raw(ptr));
} }
}).to_tokens(into); }).to_tokens(into);
} }
@ -198,21 +231,6 @@ fn bindgen(export_name: &syn::LitStr,
let i = i + offset; let i = i + offset;
let ident = syn::Ident::from(format!("arg{}", i)); let ident = syn::Ident::from(format!("arg{}", i));
match *ty { 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 => { ast::Type::BorrowedStr => {
malloc = malloc || !MALLOC_GENERATED.swap(true, Ordering::SeqCst); malloc = malloc || !MALLOC_GENERATED.swap(true, Ordering::SeqCst);
let ptr = syn::Ident::from(format!("arg{}_ptr", i)); let ptr = syn::Ident::from(format!("arg{}_ptr", i));
@ -239,90 +257,63 @@ fn bindgen(export_name: &syn::LitStr,
}; };
}); });
} }
ast::Type::ByValue(name) => { ast::Type::ByValue(ref t) => {
args.push(my_quote! { #ident: *mut ::wasm_bindgen::__rt::WasmRefCell<#name> }); args.push(my_quote! {
#ident: <#t as ::wasm_bindgen::convert::WasmBoundary >::Js
});
arg_conversions.push(my_quote! { arg_conversions.push(my_quote! {
::wasm_bindgen::__rt::assert_not_null(#ident);
let #ident = unsafe { let #ident = unsafe {
(*#ident).borrow_mut(); <#t as ::wasm_bindgen::convert::WasmBoundary>
Box::from_raw(#ident).into_inner() ::from_js(#ident)
}; };
}); });
} }
ast::Type::ByRef(name) => { ast::Type::ByRef(ref ty) => {
args.push(my_quote! { #ident: *mut ::wasm_bindgen::__rt::WasmRefCell<#name> }); args.push(my_quote! {
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
});
arg_conversions.push(my_quote! { arg_conversions.push(my_quote! {
::wasm_bindgen::__rt::assert_not_null(#ident); let #ident = unsafe {
let #ident = unsafe { (*#ident).borrow() }; <#ty as ::wasm_bindgen::convert::FromRefWasmBoundary>
::from_js_ref(#ident)
};
let #ident = &*#ident; let #ident = &*#ident;
}); });
} }
ast::Type::ByMutRef(name) => { ast::Type::ByMutRef(ref ty) => {
args.push(my_quote! { #ident: *mut ::wasm_bindgen::__rt::WasmRefCell<#name> }); args.push(my_quote! {
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
});
arg_conversions.push(my_quote! { arg_conversions.push(my_quote! {
::wasm_bindgen::__rt::assert_not_null(#ident); let mut #ident = unsafe {
let mut #ident = unsafe { (*#ident).borrow_mut() }; <#ty as ::wasm_bindgen::convert::FromRefMutWasmBoundary>
::from_js_ref_mut(#ident)
};
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 });
} }
let ret_ty; let ret_ty;
let convert_ret; let convert_ret;
match ret_type { 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) => { Some(&ast::Type::String) => {
boxed_str = !BOXED_STR_GENERATED.swap(true, Ordering::SeqCst); boxed_str = !BOXED_STR_GENERATED.swap(true, Ordering::SeqCst);
ret_ty = my_quote! { -> *mut String }; ret_ty = my_quote! { -> *mut String };
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) }; convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
} }
Some(&ast::Type::ByValue(name)) => { Some(&ast::Type::ByValue(ref t)) => {
ret_ty = my_quote! { -> *mut ::wasm_bindgen::__rt::WasmRefCell<#name> }; ret_ty = my_quote! {
-> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
};
convert_ret = my_quote! { 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) => { Some(&ast::Type::BorrowedStr) |
ret_ty = my_quote! { -> u32 }; Some(&ast::Type::ByMutRef(_)) |
convert_ret = my_quote! { Some(&ast::Type::ByRef(_)) => {
::wasm_bindgen::JsObject::__into_idx(#ret)
};
}
Some(&ast::Type::JsObjectRef) => {
panic!("can't return a borrowed ref"); panic!("can't return a borrowed ref");
} }
None => { 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 { let malloc = if malloc {
my_quote! { my_quote! {
#[no_mangle] #[no_mangle]
@ -429,8 +422,10 @@ impl ToTokens for Receiver {
} }
fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) { fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
let import_name = import.function.wasm_function.shared() let import_name = shared::mangled_import_name(
.mangled_import_name(None); None,
import.function.wasm_function.name.as_ref(),
);
bindgen_import_function(&import.function, &import_name, tokens); 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(); let mut methods = Tokens::new();
for &(_is_method, ref f) in import.functions.iter() { for &(_is_method, ref f) in import.functions.iter() {
let import_name = f.wasm_function.shared() let import_name = shared::mangled_import_name(
.mangled_import_name(Some(&import.name.to_string())); Some(&import.name.to_string()),
f.wasm_function.name.as_ref(),
);
bindgen_import_function(f, &import_name, &mut methods); 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) { for (ty, name) in import.wasm_function.arguments.iter().zip(names) {
match *ty { 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 => { ast::Type::BorrowedStr => {
let ptr = syn::Ident::from(format!("{}_ptr", name)); let ptr = syn::Ident::from(format!("{}_ptr", name));
let len = syn::Ident::from(format!("{}_len", name)); let len = syn::Ident::from(format!("{}_len", name));
@ -526,59 +503,70 @@ fn bindgen_import_function(import: &ast::ImportFunction,
let #len = #name.len(); 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_argument_names.push(name);
abi_arguments.push(my_quote! { #name: u32 }); abi_arguments.push(my_quote! { #name: u32 });
arg_conversions.push(my_quote! { 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 => { // TODO: need to test this
abi_argument_names.push(name); ast::Type::String => {
abi_arguments.push(my_quote! { #name: u32 }); 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! { 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 abi_ret;
let convert_ret; let convert_ret;
match import.wasm_function.ret { match import.wasm_function.ret {
Some(ast::Type::Integer(i)) => { Some(ast::Type::ByValue(ref t)) => {
abi_ret = my_quote! { #i }; abi_ret = my_quote! {
convert_ret = my_quote! { #ret_ident }; <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
} };
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 };
convert_ret = my_quote! { 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"), // TODO: add a test for this
Some(ast::Type::ByRef(_)) => panic!("can't return a borrowed ref"), 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::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 => { None => {
abi_ret = my_quote! { () }; abi_ret = my_quote! { () };
convert_ret = my_quote! {}; convert_ret = my_quote! {};

View File

@ -5,8 +5,9 @@ extern crate serde_derive;
pub struct Program { pub struct Program {
pub structs: Vec<Struct>, pub structs: Vec<Struct>,
pub free_functions: Vec<Function>, pub free_functions: Vec<Function>,
pub imports: Vec<(String, Function)>, pub imports: Vec<Import>,
pub imported_structs: Vec<ImportStruct>, pub imported_structs: Vec<ImportStruct>,
pub custom_type_names: Vec<String>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -16,11 +17,23 @@ pub struct Struct {
pub methods: Vec<Method>, pub methods: Vec<Method>,
} }
#[derive(Serialize, Deserialize)]
pub struct Import {
pub module: String,
pub function: Function,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ImportStruct { pub struct ImportStruct {
pub module: Option<String>, pub module: Option<String>,
pub name: 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)] #[derive(Serialize, Deserialize)]
@ -36,58 +49,66 @@ pub struct Function {
pub ret: Option<Type>, pub ret: Option<Type>,
} }
impl Struct { pub fn free_function(struct_name: &str) -> String {
pub fn free_function(&self) -> String { let mut name = format!("__wbindgen_");
let mut name = format!("__wbindgen_"); name.extend(struct_name
name.extend(self.name .chars()
.chars() .flat_map(|s| s.to_lowercase()));
.flat_map(|s| s.to_lowercase())); name.push_str("_free");
name.push_str("_free"); return name
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 type Type = char;
pub fn free_function_export_name(&self) -> String {
self.name.clone()
}
pub fn struct_function_export_name(&self, struct_: &str) -> String { pub const TYPE_NUMBER: char = '\u{5e}';
let mut name = struct_ pub const TYPE_BORROWED_STR: char = '\u{5f}';
.chars() pub const TYPE_STRING: char = '\u{60}';
.flat_map(|s| s.to_lowercase()) pub const TYPE_BOOLEAN: char = '\u{61}';
.collect::<String>(); pub const TYPE_JS_OWNED: char = '\u{62}';
name.push_str("_"); pub const TYPE_JS_REF: char = '\u{63}';
name.push_str(&self.name);
return name
}
pub fn mangled_import_name(&self, struct_: Option<&str>) -> String { pub const TYPE_CUSTOM_START: u32 = 0x64;
match struct_ { pub const TYPE_CUSTOM_REF_FLAG: u32 = 1;
Some(s) => format!("__wbg_s_{}_{}", s, self.name),
None => format!("__wbg_f_{}", self.name),
}
}
}
#[derive(Serialize, Deserialize)] // #[derive(Serialize, Deserialize)]
pub enum Type { // pub enum Type {
Number, // Number,
BorrowedStr, // BorrowedStr,
String, // String,
ByValue(String), // ByValue(String), // wrapper class
ByRef(String), // ByRef(String), // wrapper class
ByMutRef(String), // ByMutRef(String), // wrapper class
JsObject, // JsObject,
JsObjectRef, // JsObjectRef,
Boolean, // Boolean,
} // }
impl Type { // impl Type {
pub fn is_number(&self) -> bool { // pub fn is_number(&self) -> bool {
match *self { // match *self {
Type::Number => true, // Type::Number => true,
_ => false, // _ => false,
} // }
} // }
} // }

122
src/convert.rs Normal file
View 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 })
}
}

View File

@ -8,7 +8,6 @@
extern crate wasm_bindgen_macro; extern crate wasm_bindgen_macro;
use std::mem;
use std::ptr; use std::ptr;
/// A module which is typically glob imported from: /// A module which is typically glob imported from:
@ -21,6 +20,8 @@ pub mod prelude {
pub use JsObject; pub use JsObject;
} }
pub mod convert;
/// Representation of an object owned by JS. /// Representation of an object owned by JS.
/// ///
/// A `JsObject` doesn't actually live in Rust right now but actually in a table /// 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. /// be owned by the JS garbage collector.
pub fn from_str(s: &str) -> JsObject { pub fn from_str(s: &str) -> JsObject {
unsafe { 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. /// allocated number) and returns a handle to the JS version of it.
pub fn from_f64(n: f64) -> JsObject { pub fn from_f64(n: f64) -> JsObject {
unsafe { 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. /// allocated boolean) and returns a handle to the JS version of it.
pub fn from_bool(b: bool) -> JsObject { pub fn from_bool(b: bool) -> JsObject {
unsafe { 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`. /// Creates a new JS value representing `undefined`.
pub fn undefined() -> JsObject { pub fn undefined() -> JsObject {
unsafe { unsafe {
JsObject::__from_idx(__wbindgen_undefined_new()) JsObject { idx: __wbindgen_undefined_new() }
} }
} }
/// Creates a new JS value representing `null`. /// Creates a new JS value representing `null`.
pub fn null() -> JsObject { pub fn null() -> JsObject {
unsafe { unsafe {
JsObject::__from_idx(__wbindgen_null_new()) JsObject { idx: __wbindgen_null_new() }
} }
} }
@ -84,26 +85,26 @@ impl JsObject {
unsafe { unsafe {
let ptr = description.map(|s| s.as_ptr()).unwrap_or(ptr::null()); let ptr = description.map(|s| s.as_ptr()).unwrap_or(ptr::null());
let len = description.map(|s| s.len()).unwrap_or(0); 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)] // #[doc(hidden)]
pub fn __from_idx(idx: u32) -> JsObject { // pub fn __from_idx(idx: u32) -> JsObject {
JsObject { idx } // JsObject { idx }
} // }
//
#[doc(hidden)] // #[doc(hidden)]
pub fn __get_idx(&self) -> u32 { // pub fn __get_idx(&self) -> u32 {
self.idx // self.idx
} // }
//
#[doc(hidden)] // #[doc(hidden)]
pub fn __into_idx(self) -> u32 { // pub fn __into_idx(self) -> u32 {
let ret = self.idx; // let ret = self.idx;
mem::forget(self); // mem::forget(self);
return ret // return ret
} // }
/// Returns the `f64` value of this JS value if it's an instance of a /// Returns the `f64` value of this JS value if it's an instance of a
/// number. /// number.

View File

@ -1,125 +1,125 @@
extern crate test_support; extern crate test_support;
#[test] // #[test]
fn simple() { // fn simple() {
test_support::project() // test_support::project()
.file("src/lib.rs", r#" // .file("src/lib.rs", r#"
#![feature(proc_macro)] // #![feature(proc_macro)]
//
extern crate wasm_bindgen; // extern crate wasm_bindgen;
//
use wasm_bindgen::prelude::*; // use wasm_bindgen::prelude::*;
//
wasm_bindgen! { // wasm_bindgen! {
extern struct Math { // extern struct Math {
fn random() -> f64; // fn random() -> f64;
fn log(a: f64) -> f64; // fn log(a: f64) -> f64;
} // }
//
pub fn get_random() -> f64 { // pub fn get_random() -> f64 {
Math::random() // Math::random()
} // }
//
pub fn do_log(a: f64) -> f64 { // pub fn do_log(a: f64) -> f64 {
Math::log(a) // Math::log(a)
} // }
} // }
"#) // "#)
.file("test.ts", r#" // .file("test.ts", r#"
import * as wasm from "./out"; // import * as wasm from "./out";
import * as assert from "assert"; // import * as assert from "assert";
//
export function test() { // export function test() {
wasm.get_random(); // wasm.get_random();
assert.strictEqual(wasm.do_log(1.0), Math.log(1.0)); // assert.strictEqual(wasm.do_log(1.0), Math.log(1.0));
} // }
"#) // "#)
.test(); // .test();
} // }
//
#[test] // #[test]
fn import_class() { // fn import_class() {
test_support::project() // test_support::project()
.file("src/lib.rs", r#" // .file("src/lib.rs", r#"
#![feature(proc_macro)] // #![feature(proc_macro)]
//
extern crate wasm_bindgen; // extern crate wasm_bindgen;
//
use wasm_bindgen::prelude::*; // use wasm_bindgen::prelude::*;
//
wasm_bindgen! { // wasm_bindgen! {
#[wasm_module = "./test"] // #[wasm_module = "./test"]
extern struct Foo { // extern struct Foo {
fn bar(); // fn bar();
} // }
//
pub fn bar() { // pub fn bar() {
Foo::bar(); // Foo::bar();
} // }
} // }
"#) // "#)
.file("test.ts", r#" // .file("test.ts", r#"
import * as wasm from "./out"; // import * as wasm from "./out";
import * as assert from "assert"; // import * as assert from "assert";
//
let called = false; // let called = false;
//
export class Foo { // export class Foo {
static bar() { // static bar() {
called = true; // called = true;
} // }
} // }
//
export function test() { // export function test() {
wasm.bar(); // wasm.bar();
assert.strictEqual(called, true); // assert.strictEqual(called, true);
} // }
"#) // "#)
.test(); // .test();
} // }
//
#[test] // #[test]
fn construct() { // fn construct() {
test_support::project() // test_support::project()
.file("src/lib.rs", r#" // .file("src/lib.rs", r#"
#![feature(proc_macro)] // #![feature(proc_macro)]
//
extern crate wasm_bindgen; // extern crate wasm_bindgen;
//
use wasm_bindgen::prelude::*; // use wasm_bindgen::prelude::*;
//
wasm_bindgen! { // wasm_bindgen! {
#[wasm_module = "./test"] // #[wasm_module = "./test"]
extern struct Foo { // extern struct Foo {
fn create() -> Foo; // fn create() -> Foo;
fn doit(&self); // fn doit(&self);
} // }
//
pub fn bar() { // pub fn bar() {
let foo = Foo::bar(); // let foo = Foo::bar();
} // }
} // }
"#) // "#)
.file("test.ts", r#" // .file("test.ts", r#"
import * as wasm from "./out"; // import * as wasm from "./out";
import * as assert from "assert"; // import * as assert from "assert";
//
let called = false; // let called = false;
//
export class Foo { // export class Foo {
static create() { // static create() {
return new Foo(); // return new Foo();
} // }
//
doit() { // doit() {
called = true; // called = true;
} // }
} // }
//
export function test() { // export function test() {
wasm.bar(); // wasm.bar();
assert.strictEqual(called, true); // assert.strictEqual(called, true);
} // }
"#) // "#)
.test(); // .test();
} // }