Split out the AST and codegen into the wasm-bindgen-backend crate

This commit is contained in:
Nick Fitzgerald 2018-03-29 09:14:32 -07:00
parent 79ef5979f4
commit 0bd54480c6
7 changed files with 860 additions and 846 deletions

10
crates/backend/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "wasm-bindgen-backend"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
[dependencies]
quote = '0.4'
proc-macro2 = { version = "0.2", features = ["nightly"] }
wasm-bindgen-shared = { path = "../shared", version = "0.1.0" }
syn = { version = '0.12', features = ['full'] }

View File

@ -1,6 +1,6 @@
use literal::{self, Literal}; use literal::{self, Literal};
use proc_macro2::Span; use proc_macro2::Span;
use quote::{Tokens, ToTokens}; use quote::{ToTokens, Tokens};
use shared; use shared;
use syn; use syn;
@ -39,14 +39,8 @@ pub struct ImportFunction {
} }
pub enum ImportFunctionKind { pub enum ImportFunctionKind {
Method { Method { class: String, ty: syn::Type },
class: String, JsConstructor { class: String, ty: syn::Type },
ty: syn::Type,
},
JsConstructor {
class: String,
ty: syn::Type,
},
Normal, Normal,
} }
@ -79,7 +73,7 @@ pub struct Struct {
pub struct Enum { pub struct Enum {
pub name: syn::Ident, pub name: syn::Ident,
pub variants: Vec<Variant> pub variants: Vec<Variant>,
} }
pub struct Variant { pub struct Variant {
@ -110,20 +104,20 @@ pub enum VectorType {
} }
impl Program { impl Program {
pub fn push_item(&mut self, pub fn push_item(&mut self, item: syn::Item, opts: Option<BindgenAttrs>, tokens: &mut Tokens) {
item: syn::Item,
opts: Option<BindgenAttrs>,
tokens: &mut Tokens) {
match item { match item {
syn::Item::Fn(mut f) => { syn::Item::Fn(mut f) => {
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs)); let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
let no_mangle = f.attrs.iter() let no_mangle = f.attrs
.iter()
.enumerate() .enumerate()
.filter_map(|(i, m)| m.interpret_meta().map(|m| (i, m))) .filter_map(|(i, m)| m.interpret_meta().map(|m| (i, m)))
.find(|&(_, ref m)| m.name() == "no_mangle"); .find(|&(_, ref m)| m.name() == "no_mangle");
match no_mangle { match no_mangle {
Some((i, _)) => { f.attrs.remove(i); } Some((i, _)) => {
f.attrs.remove(i);
}
_ => {} _ => {}
} }
f.to_tokens(tokens); f.to_tokens(tokens);
@ -153,8 +147,10 @@ impl Program {
e.to_tokens(tokens); e.to_tokens(tokens);
self.push_enum(e, opts); self.push_enum(e, opts);
} }
_ => panic!("#[wasm_bindgen] can only be applied to a function, \ _ => panic!(
struct, enum, impl, or extern block"), "#[wasm_bindgen] can only be applied to a function, \
struct, enum, impl, or extern block"
),
} }
} }
@ -172,12 +168,13 @@ impl Program {
panic!("generic impls aren't supported"); panic!("generic impls aren't supported");
} }
let name = match *item.self_ty { let name = match *item.self_ty {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => { syn::Type::Path(syn::TypePath {
match extract_path_ident(path) { qself: None,
ref path,
}) => match extract_path_ident(path) {
Some(ident) => ident, Some(ident) => ident,
None => panic!("unsupported self type in impl"), None => panic!("unsupported self type in impl"),
} },
}
_ => panic!("unsupported self type in impl"), _ => panic!("unsupported self type in impl"),
}; };
for item in item.items.into_iter() { for item in item.items.into_iter() {
@ -209,12 +206,14 @@ impl Program {
let opts = BindgenAttrs::find(&mut method.attrs); let opts = BindgenAttrs::find(&mut method.attrs);
let (function, mutable) = Function::from_decl(method.sig.ident, let (function, mutable) = Function::from_decl(
method.sig.ident,
Box::new(method.sig.decl), Box::new(method.sig.decl),
method.attrs, method.attrs,
opts, opts,
method.vis, method.vis,
true); true,
);
self.exports.push(Export { self.exports.push(Export {
class: Some(class), class: Some(class),
method: mutable.is_some(), method: mutable.is_some(),
@ -229,30 +228,40 @@ impl Program {
_ => panic!("only public enums are allowed"), _ => panic!("only public enums are allowed"),
} }
let variants = item.variants.iter().enumerate().map(|(i, v)| { let variants = item.variants
.iter()
.enumerate()
.map(|(i, v)| {
match v.fields { match v.fields {
syn::Fields::Unit => (), syn::Fields::Unit => (),
_ => panic!("Only C-Style enums allowed") _ => panic!("Only C-Style enums allowed"),
} }
let value = match v.discriminant { let value = match v.discriminant {
Some((_, syn::Expr::Lit(syn::ExprLit {attrs: _, lit: syn::Lit::Int(ref int_lit)}))) => { Some((
_,
syn::Expr::Lit(syn::ExprLit {
attrs: _,
lit: syn::Lit::Int(ref int_lit),
}),
)) => {
if int_lit.value() > <u32>::max_value() as u64 { if int_lit.value() > <u32>::max_value() as u64 {
panic!("Enums can only support numbers that can be represented as u32"); panic!("Enums can only support numbers that can be represented as u32");
} }
int_lit.value() as u32 int_lit.value() as u32
}, }
None => i as u32, None => i as u32,
_ => panic!("Enums may only have number literal values") _ => panic!("Enums may only have number literal values"),
}; };
Variant { Variant {
name: v.ident, name: v.ident,
value, value,
} }
}).collect(); })
.collect();
self.enums.push(Enum { self.enums.push(Enum {
name: item.ident, name: item.ident,
variants variants,
}); });
} }
@ -281,20 +290,17 @@ impl Program {
_ => panic!("only foreign functions/types allowed for now"), _ => panic!("only foreign functions/types allowed for now"),
}; };
self.imports.push(Import { module, js_namespace, kind }); self.imports.push(Import {
module,
js_namespace,
kind,
});
} }
} }
pub fn push_foreign_fn(&mut self, f: syn::ForeignItemFn, opts: BindgenAttrs) pub fn push_foreign_fn(&mut self, f: syn::ForeignItemFn, opts: BindgenAttrs) -> ImportKind {
-> ImportKind
{
let js_name = opts.js_name().unwrap_or(f.ident); let js_name = opts.js_name().unwrap_or(f.ident);
let mut wasm = Function::from_decl(js_name, let mut wasm = Function::from_decl(js_name, f.decl, f.attrs, opts, f.vis, false).0;
f.decl,
f.attrs,
opts,
f.vis,
false).0;
if wasm.opts.catch() { if wasm.opts.catch() {
// TODO: this assumes a whole bunch: // TODO: this assumes a whole bunch:
// //
@ -308,20 +314,19 @@ impl Program {
} }
let kind = if wasm.opts.method() { let kind = if wasm.opts.method() {
let class = wasm.arguments.get(0) let class = wasm.arguments
.get(0)
.expect("methods must have at least one argument"); .expect("methods must have at least one argument");
let class = match *class { let class = match *class {
Type::ByRef(ref t) | Type::ByRef(ref t) | Type::ByValue(ref t) => t,
Type::ByValue(ref t) => t, Type::ByMutRef(_) => panic!("first method argument cannot be mutable ref"),
Type::ByMutRef(_) => { Type::Vector(..) => panic!("method receivers cannot be vectors"),
panic!("first method argument cannot be mutable ref")
}
Type::Vector(..) => {
panic!("method receivers cannot be vectors")
}
}; };
let class_name = match *class { let class_name = match *class {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, syn::Type::Path(syn::TypePath {
qself: None,
ref path,
}) => path,
_ => panic!("first argument of method must be a path"), _ => panic!("first argument of method must be a path"),
}; };
let class_name = extract_path_ident(class_name) let class_name = extract_path_ident(class_name)
@ -337,7 +342,10 @@ impl Program {
_ => panic!("constructor returns must be bare types"), _ => panic!("constructor returns must be bare types"),
}; };
let class_name = match *class { let class_name = match *class {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, syn::Type::Path(syn::TypePath {
qself: None,
ref path,
}) => path,
_ => panic!("first argument of method must be a path"), _ => panic!("first argument of method must be a path"),
}; };
let class_name = extract_path_ident(class_name) let class_name = extract_path_ident(class_name)
@ -367,20 +375,18 @@ impl Program {
}) })
} }
pub fn push_foreign_ty(&mut self, f: syn::ForeignItemType) pub fn push_foreign_ty(&mut self, f: syn::ForeignItemType) -> ImportKind {
-> ImportKind
{
ImportKind::Type(ImportType { ImportKind::Type(ImportType {
vis: f.vis, vis: f.vis,
name: f.ident name: f.ident,
}) })
} }
pub fn push_foreign_static(&mut self, pub fn push_foreign_static(
&mut self,
f: syn::ForeignItemStatic, f: syn::ForeignItemStatic,
opts: BindgenAttrs) opts: BindgenAttrs,
-> ImportKind ) -> ImportKind {
{
if f.mutability.is_some() { if f.mutability.is_some() {
panic!("cannot import mutable globals yet") panic!("cannot import mutable globals yet")
} }
@ -427,20 +433,17 @@ impl Function {
panic!("can only bindgen safe functions"); panic!("can only bindgen safe functions");
} }
Function::from_decl(input.ident, Function::from_decl(input.ident, input.decl, input.attrs, opts, input.vis, false).0
input.decl,
input.attrs,
opts,
input.vis,
false).0
} }
pub fn from_decl(name: syn::Ident, pub fn from_decl(
name: syn::Ident,
decl: Box<syn::FnDecl>, decl: Box<syn::FnDecl>,
attrs: Vec<syn::Attribute>, attrs: Vec<syn::Attribute>,
opts: BindgenAttrs, opts: BindgenAttrs,
vis: syn::Visibility, vis: syn::Visibility,
allow_self: bool) -> (Function, Option<bool>) { allow_self: bool,
) -> (Function, Option<bool>) {
if decl.variadic.is_some() { if decl.variadic.is_some() {
panic!("can't bindgen variadic functions") panic!("can't bindgen variadic functions")
} }
@ -449,9 +452,9 @@ impl Function {
} }
let mut mutable = None; let mut mutable = None;
let arguments = decl.inputs.iter() let arguments = decl.inputs
.filter_map(|arg| { .iter()
match *arg { .filter_map(|arg| match *arg {
syn::FnArg::Captured(ref c) => Some(c), syn::FnArg::Captured(ref c) => Some(c),
syn::FnArg::SelfValue(_) => { syn::FnArg::SelfValue(_) => {
panic!("by-value `self` not yet supported"); panic!("by-value `self` not yet supported");
@ -462,7 +465,6 @@ impl Function {
None None
} }
_ => panic!("arguments cannot be `self` or ignored"), _ => panic!("arguments cannot be `self` or ignored"),
}
}) })
.map(|arg| Type::from(&arg.ty)) .map(|arg| Type::from(&arg.ty))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -472,7 +474,8 @@ impl Function {
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)), syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
}; };
(Function { (
Function {
name, name,
arguments, arguments,
ret, ret,
@ -480,16 +483,18 @@ impl Function {
rust_vis: vis, rust_vis: vis,
rust_decl: decl, rust_decl: decl,
rust_attrs: attrs, rust_attrs: attrs,
}, mutable) },
mutable,
)
} }
} }
pub fn extract_path_ident(path: &syn::Path) -> Option<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() {
return None return None;
} }
if path.segments.len() != 1 { if path.segments.len() != 1 {
return None return None;
} }
match path.segments.first().unwrap().value().arguments { match path.segments.first().unwrap().value().arguments {
syn::PathArguments::None => {} syn::PathArguments::None => {}
@ -503,7 +508,10 @@ impl Type {
match *ty { match *ty {
syn::Type::Reference(ref r) => { syn::Type::Reference(ref r) => {
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().map(|s| s.as_ref()) { match ident.as_ref().map(|s| s.as_ref()) {
Some("str") => return Type::Vector(VectorType::String, false), Some("str") => return Type::Vector(VectorType::String, false),
@ -512,7 +520,7 @@ impl Type {
} }
syn::Type::Slice(ref slice) => { syn::Type::Slice(ref slice) => {
if let Some(ty) = VectorType::from(&slice.elem) { if let Some(ty) = VectorType::from(&slice.elem) {
return Type::Vector(ty, false) return Type::Vector(ty, false);
} }
} }
_ => {} _ => {}
@ -521,26 +529,26 @@ impl Type {
Type::ByMutRef((*r.elem).clone()) Type::ByMutRef((*r.elem).clone())
} else { } else {
Type::ByRef((*r.elem).clone()) Type::ByRef((*r.elem).clone())
};
} }
} syn::Type::Path(syn::TypePath {
syn::Type::Path(syn::TypePath { qself: None, ref path }) qself: None,
if path.leading_colon.is_none() && path.segments.len() == 1 => ref path,
}) if path.leading_colon.is_none() && path.segments.len() == 1 =>
{ {
let seg = path.segments.first().unwrap().into_value(); let seg = path.segments.first().unwrap().into_value();
match seg.arguments { match seg.arguments {
syn::PathArguments::None => { syn::PathArguments::None => match seg.ident.as_ref() {
match seg.ident.as_ref() {
"String" => return Type::Vector(VectorType::String, true), "String" => return Type::Vector(VectorType::String, true),
_ => {} _ => {}
} },
}
syn::PathArguments::AngleBracketed(ref t) syn::PathArguments::AngleBracketed(ref t)
if seg.ident == "Vec" && t.args.len() == 1 => if seg.ident == "Vec" && t.args.len() == 1 =>
{ {
match **t.args.first().unwrap().value() { match **t.args.first().unwrap().value() {
syn::GenericArgument::Type(ref t) => { syn::GenericArgument::Type(ref t) => {
if let Some(ty) = VectorType::from(t) { if let Some(ty) = VectorType::from(t) {
return Type::Vector(ty, true) return Type::Vector(ty, true);
} }
} }
_ => {} _ => {}
@ -571,14 +579,9 @@ impl Export {
pub fn export_name(&self) -> syn::LitStr { pub fn export_name(&self) -> syn::LitStr {
let name = match self.class { let name = match self.class {
Some(class) => { Some(class) => {
shared::struct_function_export_name( shared::struct_function_export_name(class.as_ref(), self.function.name.as_ref())
class.as_ref(),
self.function.name.as_ref(),
)
}
None => {
shared::free_function_export_name(self.function.name.as_ref())
} }
None => shared::free_function_export_name(self.function.name.as_ref()),
}; };
syn::LitStr::new(&name, Span::def_site()) syn::LitStr::new(&name, Span::def_site())
} }
@ -609,7 +612,8 @@ pub struct BindgenAttrs {
impl BindgenAttrs { impl BindgenAttrs {
pub fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs { pub fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs {
let pos = attrs.iter() let pos = attrs
.iter()
.enumerate() .enumerate()
.find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen") .find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen")
.map(|a| a.0); .map(|a| a.0);
@ -617,101 +621,83 @@ impl BindgenAttrs {
Some(i) => i, Some(i) => i,
None => return BindgenAttrs::default(), None => return BindgenAttrs::default(),
}; };
syn::parse(attrs.remove(pos).tts.into()) syn::parse(attrs.remove(pos).tts.into()).expect("malformed #[wasm_bindgen] attribute")
.expect("malformed #[wasm_bindgen] attribute")
} }
fn module(&self) -> Option<&str> { fn module(&self) -> Option<&str> {
self.attrs.iter() self.attrs
.filter_map(|a| { .iter()
match *a { .filter_map(|a| match *a {
BindgenAttr::Module(ref s) => Some(&s[..]), BindgenAttr::Module(ref s) => Some(&s[..]),
_ => None, _ => None,
}
}) })
.next() .next()
} }
pub fn catch(&self) -> bool { pub fn catch(&self) -> bool {
self.attrs.iter() self.attrs.iter().any(|a| match *a {
.any(|a| {
match *a {
BindgenAttr::Catch => true, BindgenAttr::Catch => true,
_ => false, _ => false,
}
}) })
} }
fn constructor(&self) -> bool { fn constructor(&self) -> bool {
self.attrs.iter() self.attrs.iter().any(|a| match *a {
.any(|a| {
match *a {
BindgenAttr::Constructor => true, BindgenAttr::Constructor => true,
_ => false, _ => false,
}
}) })
} }
fn method(&self) -> bool { fn method(&self) -> bool {
self.attrs.iter() self.attrs.iter().any(|a| match *a {
.any(|a| {
match *a {
BindgenAttr::Method => true, BindgenAttr::Method => true,
_ => false, _ => false,
}
}) })
} }
fn js_namespace(&self) -> Option<syn::Ident> { fn js_namespace(&self) -> Option<syn::Ident> {
self.attrs.iter() self.attrs
.filter_map(|a| { .iter()
match *a { .filter_map(|a| match *a {
BindgenAttr::JsNamespace(s) => Some(s), BindgenAttr::JsNamespace(s) => Some(s),
_ => None, _ => None,
}
}) })
.next() .next()
} }
pub fn getter(&self) -> Option<Option<syn::Ident>> { pub fn getter(&self) -> Option<Option<syn::Ident>> {
self.attrs.iter() self.attrs
.filter_map(|a| { .iter()
match *a { .filter_map(|a| match *a {
BindgenAttr::Getter(s) => Some(s), BindgenAttr::Getter(s) => Some(s),
_ => None, _ => None,
}
}) })
.next() .next()
} }
pub fn setter(&self) -> Option<Option<syn::Ident>> { pub fn setter(&self) -> Option<Option<syn::Ident>> {
self.attrs.iter() self.attrs
.filter_map(|a| { .iter()
match *a { .filter_map(|a| match *a {
BindgenAttr::Setter(s) => Some(s), BindgenAttr::Setter(s) => Some(s),
_ => None, _ => None,
}
}) })
.next() .next()
} }
pub fn structural(&self) -> bool { pub fn structural(&self) -> bool {
self.attrs.iter() self.attrs.iter().any(|a| match *a {
.any(|a| {
match *a {
BindgenAttr::Structural => true, BindgenAttr::Structural => true,
_ => false, _ => false,
}
}) })
} }
pub fn js_name(&self) -> Option<syn::Ident> { pub fn js_name(&self) -> Option<syn::Ident> {
self.attrs.iter() self.attrs
.filter_map(|a| { .iter()
match *a { .filter_map(|a| match *a {
BindgenAttr::JsName(s) => Some(s), BindgenAttr::JsName(s) => Some(s),
_ => None, _ => None,
}
}) })
.next() .next()
} }
@ -800,14 +786,17 @@ impl syn::synom::Synom for BindgenAttr {
fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> { fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
let ty = match ty { let ty = match ty {
Some(t) => t, Some(t) => t,
None => return Some(None) None => return Some(None),
}; };
let ty = match *ty { let ty = match *ty {
Type::ByValue(ref t) => t, Type::ByValue(ref t) => t,
_ => return None, _ => return None,
}; };
let path = match *ty { let path = match *ty {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, syn::Type::Path(syn::TypePath {
qself: None,
ref path,
}) => path,
_ => return None, _ => return None,
}; };
let seg = path.segments.last()?.into_value(); let seg = path.segments.last()?.into_value();
@ -826,12 +815,10 @@ fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
Some(Some(Type::from(ty))) Some(Some(Type::from(ty)))
} }
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> {
-> syn::synom::PResult<'a, ()>
{
if let Some((_span, term, next)) = cursor.term() { if let Some((_span, term, next)) = cursor.term() {
if term.as_str() == name { if term.as_str() == name {
return Ok(((), next)) return Ok(((), next));
} }
} }
syn::parse_error() syn::parse_error()
@ -847,7 +834,10 @@ fn ungroup(input: &syn::Type) -> &syn::Type {
impl VectorType { impl VectorType {
fn from(ty: &syn::Type) -> Option<VectorType> { fn from(ty: &syn::Type) -> Option<VectorType> {
let path = match *ungroup(ty) { let path = match *ungroup(ty) {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, syn::Type::Path(syn::TypePath {
qself: None,
ref path,
}) => path,
_ => return None, _ => return None,
}; };
match extract_path_ident(path)?.as_ref() { match extract_path_ident(path)?.as_ref() {

View File

@ -0,0 +1,621 @@
use ast;
use quote::{ToTokens, Tokens};
use proc_macro2::Span;
use shared;
use std::borrow::Cow;
use std::collections::HashSet;
use std::env;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use syn;
fn to_ident_name(s: &str) -> Cow<str> {
if s.chars().all(|c| match c {
'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => true,
_ => false,
}) {
return Cow::from(s);
}
Cow::from(
s.chars()
.map(|c| match c {
'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => c,
_ => '_',
})
.collect::<String>(),
)
}
impl ToTokens for ast::Program {
// Generate wrappers for all the items that we've found
fn to_tokens(&self, tokens: &mut Tokens) {
for export in self.exports.iter() {
export.to_tokens(tokens);
}
for s in self.structs.iter() {
s.to_tokens(tokens);
}
let mut types = HashSet::new();
for i in self.imports.iter() {
if let ast::ImportKind::Type(ref t) = i.kind {
types.insert(t.name);
}
}
for i in self.imports.iter() {
match i.js_namespace {
Some(ns) if types.contains(&ns) => {
let kind = &i.kind;
(quote! { impl #ns { #kind } }).to_tokens(tokens);
}
_ => i.kind.to_tokens(tokens),
}
}
for e in self.enums.iter() {
e.to_tokens(tokens);
}
// Generate a static which will eventually be what lives in a custom section
// of the wasm executable. For now it's just a plain old static, but we'll
// eventually have it actually in its own section.
static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
let crate_name = env::var("CARGO_PKG_NAME").expect("should have CARGO_PKG_NAME env var");
let crate_vers =
env::var("CARGO_PKG_VERSION").expect("should have CARGO_PKG_VERSION env var");
let generated_static_name = format!(
"__WASM_BINDGEN_GENERATED_{}_{}_{}",
to_ident_name(&crate_name),
to_ident_name(&crate_vers),
CNT.fetch_add(1, Ordering::SeqCst)
);
let generated_static_name = syn::Ident::from(generated_static_name);
let mut generated_static_value = Tokens::new();
let generated_static_length = self.literal(&mut generated_static_value);
(my_quote! {
#[allow(non_upper_case_globals)]
#[wasm_custom_section = "__wasm_bindgen_unstable"]
const #generated_static_name: [u8; #generated_static_length] =
[#generated_static_value];
}).to_tokens(tokens);
}
}
impl ToTokens for ast::Struct {
fn to_tokens(&self, tokens: &mut Tokens) {
let name = &self.name;
let free_fn = syn::Ident::from(shared::free_function(self.name.as_ref()));
let c = shared::name_to_descriptor(name.as_ref()) as u32;
(my_quote! {
impl ::wasm_bindgen::convert::WasmBoundary for #name {
type Js = u32;
const DESCRIPTOR: u32 = #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: u32) {
<#name as ::wasm_bindgen::convert::WasmBoundary>::from_js(ptr);
}
}).to_tokens(tokens);
}
}
impl ToTokens for ast::Export {
fn to_tokens(self: &ast::Export, into: &mut Tokens) {
let generated_name = self.rust_symbol();
let export_name = self.export_name();
let mut args = vec![];
let mut arg_conversions = vec![];
let mut converted_arguments = vec![];
let ret = syn::Ident::from("_ret");
let mut offset = 0;
if self.method {
let class = self.class.unwrap();
args.push(my_quote! { me: *mut ::wasm_bindgen::__rt::WasmRefCell<#class> });
arg_conversions.push(my_quote! {
::wasm_bindgen::__rt::assert_not_null(me);
let me = unsafe { &*me };
});
offset = 1;
}
for (i, ty) in self.function.arguments.iter().enumerate() {
let i = i + offset;
let ident = syn::Ident::from(format!("arg{}", i));
match *ty {
ast::Type::Vector(ref ty, owned) => {
let ptr = syn::Ident::from(format!("arg{}_ptr", i));
let len = syn::Ident::from(format!("arg{}_len", i));
let abi_ty = ty.abi_element();
args.push(my_quote! { #ptr: *mut #abi_ty });
args.push(my_quote! { #len: usize });
if owned {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::vec::Vec::from_raw_parts(#ptr, #len, #len)
};
});
} else {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::slice::from_raw_parts(#ptr as *const #abi_ty, #len)
};
});
}
if let ast::VectorType::String = *ty {
if owned {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::string::String::from_utf8_unchecked(#ident)
};
});
} else {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::str::from_utf8_unchecked(#ident)
};
});
}
}
}
ast::Type::ByValue(ref t) => {
args.push(my_quote! {
#ident: <#t as ::wasm_bindgen::convert::WasmBoundary >::Js
});
arg_conversions.push(my_quote! {
let #ident = unsafe {
<#t as ::wasm_bindgen::convert::WasmBoundary>
::from_js(#ident)
};
});
}
ast::Type::ByRef(ref ty) => {
args.push(my_quote! {
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
});
arg_conversions.push(my_quote! {
let #ident = unsafe {
<#ty as ::wasm_bindgen::convert::FromRefWasmBoundary>
::from_js_ref(#ident)
};
let #ident = &*#ident;
});
}
ast::Type::ByMutRef(ref ty) => {
args.push(my_quote! {
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
});
arg_conversions.push(my_quote! {
let mut #ident = unsafe {
<#ty as ::wasm_bindgen::convert::FromRefMutWasmBoundary>
::from_js_ref_mut(#ident)
};
let #ident = &mut *#ident;
});
}
}
converted_arguments.push(my_quote! { #ident });
}
let ret_ty;
let convert_ret;
match self.function.ret {
Some(ast::Type::Vector(ref ty, true)) => {
ret_ty = my_quote! { -> *mut #ty };
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
}
Some(ast::Type::ByValue(ref t)) => {
ret_ty = my_quote! {
-> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
};
convert_ret = my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret)
};
}
Some(ast::Type::Vector(_, false))
| Some(ast::Type::ByMutRef(_))
| Some(ast::Type::ByRef(_)) => {
panic!("can't return a borrowed ref");
}
None => {
ret_ty = my_quote!{};
convert_ret = my_quote!{};
}
}
let name = self.function.name;
let receiver = match self.class {
Some(_) if self.method => {
if self.mutable {
my_quote! { me.borrow_mut().#name }
} else {
my_quote! { me.borrow().#name }
}
}
Some(class) => my_quote! { #class::#name },
None => my_quote!{ #name },
};
let tokens = my_quote! {
#[export_name = #export_name]
#[allow(non_snake_case)]
pub extern fn #generated_name(#(#args),*) #ret_ty {
::wasm_bindgen::__rt::link_this_library();
#(#arg_conversions)*
let #ret = #receiver(#(#converted_arguments),*);
#convert_ret
}
};
tokens.to_tokens(into);
}
}
impl ToTokens for ast::ImportType {
fn to_tokens(&self, tokens: &mut Tokens) {
let vis = &self.vis;
let name = &self.name;
(my_quote! {
#[allow(bad_style)]
#vis struct #name {
obj: ::wasm_bindgen::JsValue,
}
impl ::wasm_bindgen::convert::WasmBoundary for #name {
type Js = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::Js;
const DESCRIPTOR: u32 = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR;
fn into_js(self) -> Self::Js {
self.obj.into_js()
}
unsafe fn from_js(js: Self::Js) -> Self {
#name { obj: ::wasm_bindgen::JsValue::from_js(js) }
}
}
impl ::wasm_bindgen::convert::ToRefWasmBoundary for #name {
fn to_js_ref(&self) -> u32 {
self.obj.to_js_ref()
}
}
impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name {
type RefAnchor = ::std::mem::ManuallyDrop<#name>;
unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor {
let obj = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary>
::from_js(js);
::std::mem::ManuallyDrop::new(#name { obj })
}
}
impl From<::wasm_bindgen::JsValue> for #name {
fn from(obj: ::wasm_bindgen::JsValue) -> #name {
#name { obj }
}
}
impl From<#name> for ::wasm_bindgen::JsValue {
fn from(obj: #name) -> ::wasm_bindgen::JsValue {
obj.obj
}
}
}).to_tokens(tokens);
}
}
impl ToTokens for ast::ImportKind {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
ast::ImportKind::Function(ref f) => f.to_tokens(tokens),
ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
ast::ImportKind::Type(ref t) => t.to_tokens(tokens),
}
}
}
impl ToTokens for ast::ImportFunction {
fn to_tokens(&self, tokens: &mut Tokens) {
let mut class_ty = None;
let mut is_method = false;
match self.kind {
ast::ImportFunctionKind::Method { ref ty, .. } => {
is_method = true;
class_ty = Some(ty);
}
ast::ImportFunctionKind::JsConstructor { ref ty, .. } => {
class_ty = Some(ty);
}
ast::ImportFunctionKind::Normal => {}
}
let vis = &self.function.rust_vis;
let ret = &self.function.rust_decl.output;
let fn_token = &self.function.rust_decl.fn_token;
let mut abi_argument_names = Vec::new();
let mut abi_arguments = Vec::new();
let mut arg_conversions = Vec::new();
let ret_ident = syn::Ident::from("_ret");
let names = self.function
.rust_decl
.inputs
.iter()
.map(|arg| match *arg {
syn::FnArg::Captured(ref c) => c,
_ => panic!("arguments cannot be `self` or ignored"),
})
.map(|arg| match arg.pat {
syn::Pat::Ident(syn::PatIdent {
by_ref: None,
ident,
subpat: None,
..
}) => ident,
_ => panic!("unsupported pattern in foreign function"),
});
for (i, (ty, name)) in self.function.arguments.iter().zip(names).enumerate() {
match *ty {
ast::Type::Vector(ref ty, owned) => {
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);
let abi_ty = ty.abi_element();
abi_arguments.push(my_quote! { #ptr: *const #abi_ty });
abi_arguments.push(my_quote! { #len: usize });
arg_conversions.push(my_quote! {
let #ptr = #name.as_ptr();
let #len = #name.len();
});
if owned {
arg_conversions.push(my_quote! { ::std::mem::forget(#name); });
}
}
ast::Type::ByValue(ref t) => {
abi_argument_names.push(name);
abi_arguments.push(my_quote! {
#name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
});
if i == 0 && is_method {
arg_conversions.push(my_quote! {
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
::into_js(self);
});
} else {
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 });
if i == 0 && is_method {
arg_conversions.push(my_quote! {
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
::to_js_ref(self);
});
} else {
arg_conversions.push(my_quote! {
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
::to_js_ref(#name);
});
}
}
}
}
let abi_ret;
let mut convert_ret;
match self.function.ret {
Some(ast::Type::ByValue(ref t)) => {
abi_ret = my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::Js
};
convert_ret = my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::from_js(#ret_ident)
};
}
Some(ast::Type::Vector(ref ty, true)) => {
let name = syn::Ident::from("__ret_len");
let name_ptr = syn::Ident::from("__ret_len_ptr");
abi_argument_names.push(name_ptr);
abi_arguments.push(my_quote! { #name_ptr: *mut usize });
arg_conversions.push(my_quote! {
let mut #name = 0;
let mut #name_ptr = &mut #name as *mut usize;
});
let abi_ty = ty.abi_element();
abi_ret = my_quote! { *mut #abi_ty };
if let ast::VectorType::String = *ty {
convert_ret = my_quote! {
String::from_utf8_unchecked(
Vec::from_raw_parts(#ret_ident, #name, #name)
)
};
} else {
convert_ret = my_quote! {
Vec::from_raw_parts(#ret_ident, #name, #name)
};
}
}
Some(ast::Type::ByRef(_))
| Some(ast::Type::Vector(_, false))
| Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
None => {
abi_ret = my_quote! { () };
convert_ret = my_quote! { () };
}
}
let mut exceptional_ret = my_quote!{};
if self.function.opts.catch() {
let exn_data = syn::Ident::from("exn_data");
let exn_data_ptr = syn::Ident::from("exn_data_ptr");
abi_argument_names.push(exn_data_ptr);
abi_arguments.push(my_quote! { #exn_data_ptr: *mut u32 });
arg_conversions.push(my_quote! {
let mut #exn_data = [0; 2];
let #exn_data_ptr = #exn_data.as_mut_ptr();
});
convert_ret = my_quote! { Ok(#convert_ret) };
exceptional_ret = my_quote! {
if #exn_data[0] == 1 {
return Err(<::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::from_js(#exn_data[1]))
}
};
}
let rust_name = self.rust_name;
let import_name = self.shim;
let attrs = &self.function.rust_attrs;
let arguments = self.function
.rust_decl
.inputs
.iter()
.skip(if is_method { 1 } else { 0 })
.collect::<Vec<_>>();
let me = if is_method {
my_quote! { &self, }
} else {
quote!()
};
let invocation = my_quote! {
#(#attrs)*
#[allow(bad_style)]
#vis extern #fn_token #rust_name(#me #(#arguments),*) #ret {
::wasm_bindgen::__rt::link_this_library();
#[wasm_import_module = "__wbindgen_placeholder__"]
extern {
fn #import_name(#(#abi_arguments),*) -> #abi_ret;
}
unsafe {
#(#arg_conversions)*
let #ret_ident = #import_name(#(#abi_argument_names),*);
#exceptional_ret
#convert_ret
}
}
};
if let Some(class) = class_ty {
(quote! {
impl #class {
#invocation
}
}).to_tokens(tokens);
} else {
invocation.to_tokens(tokens);
}
}
}
impl ToTokens for ast::Enum {
fn to_tokens(&self, into: &mut Tokens) {
let enum_name = &self.name;
let c = shared::TYPE_ENUM as u32;
let incoming_u32 = quote! { n };
let enum_name_as_string = enum_name.to_string();
let cast_clauses = self.variants.iter().map(|variant| {
let variant_name = &variant.name;
quote! {
if #incoming_u32 == #enum_name::#variant_name as u32 {
#enum_name::#variant_name
}
}
});
(my_quote! {
impl #enum_name {
fn from_u32(#incoming_u32: u32) -> #enum_name {
#(#cast_clauses else)* {
wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #enum_name_as_string));
}
}
}
impl ::wasm_bindgen::convert::WasmBoundary for #enum_name {
type Js = u32;
const DESCRIPTOR: u32 = #c;
fn into_js(self) -> u32 {
self as u32
}
unsafe fn from_js(js: u32) -> Self {
#enum_name::from_u32(js)
}
}
}).to_tokens(into);
}
}
impl ToTokens for ast::ImportStatic {
fn to_tokens(&self, into: &mut Tokens) {
let name = self.rust_name;
let ty = &self.ty;
let shim_name = self.shim;
let vis = &self.vis;
(my_quote! {
#[allow(bad_style)]
#vis static #name: ::wasm_bindgen::JsStatic<#ty> = {
fn init() -> #ty {
#[wasm_import_module = "__wbindgen_placeholder__"]
extern {
fn #shim_name() -> u32;
}
unsafe {
::wasm_bindgen::convert::WasmBoundary::from_js(#shim_name())
}
}
::wasm_bindgen::JsStatic {
__inner: ::std::cell::UnsafeCell::new(None),
__init: init,
}
};
}).to_tokens(into);
}
}

25
crates/backend/src/lib.rs Executable file
View File

@ -0,0 +1,25 @@
#![recursion_limit = "128"]
extern crate proc_macro2;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate wasm_bindgen_shared as shared;
macro_rules! my_quote {
($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*))
}
pub mod ast;
mod codegen;
mod literal;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@ -11,10 +11,7 @@ pub struct LiteralBuilder<'a> {
impl<'a> LiteralBuilder<'a> { impl<'a> LiteralBuilder<'a> {
pub fn new(dst: &'a mut Tokens) -> LiteralBuilder<'a> { pub fn new(dst: &'a mut Tokens) -> LiteralBuilder<'a> {
LiteralBuilder { LiteralBuilder { dst, cnt: 0 }
dst,
cnt: 0,
}
} }
pub fn finish(self) -> usize { pub fn finish(self) -> usize {
@ -95,7 +92,8 @@ impl<'a> LiteralBuilder<'a> {
} }
fn list<T, F>(&mut self, list: T, mut cb: F) fn list<T, F>(&mut self, list: T, mut cb: F)
where F: FnMut(T::Item, &mut Self), where
F: FnMut(T::Item, &mut Self),
T: IntoIterator, T: IntoIterator,
{ {
self.append("["); self.append("[");
@ -316,8 +314,6 @@ impl Literal for ast::ImportStatic {
impl Literal for ast::ImportType { impl Literal for ast::ImportType {
fn literal(&self, a: &mut LiteralBuilder) { fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[ a.fields(&[("kind", &|a| a.str("type"))])
("kind", &|a| a.str("type")),
])
} }
} }

View File

@ -19,3 +19,4 @@ quote = '0.4'
proc-macro2 = { version = "0.2", features = ["nightly"] } proc-macro2 = { version = "0.2", features = ["nightly"] }
serde_json = "1" serde_json = "1"
wasm-bindgen-shared = { path = "../shared", version = "0.1.0" } wasm-bindgen-shared = { path = "../shared", version = "0.1.0" }
wasm-bindgen-backend = { path = "../backend", version = "0.1.0" }

View File

@ -1,40 +1,24 @@
#![recursion_limit = "128"]
#![feature(proc_macro)] #![feature(proc_macro)]
#[macro_use]
extern crate syn; extern crate syn;
#[macro_use]
extern crate quote; 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_backend as backend;
extern crate wasm_bindgen_shared as shared; extern crate wasm_bindgen_shared as shared;
use std::borrow::Cow;
use std::env;
use std::sync::atomic::*;
use std::collections::HashSet;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Span; use quote::{ToTokens, Tokens};
use quote::{Tokens, ToTokens};
macro_rules! my_quote {
($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*))
}
mod ast;
mod literal;
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
let item = syn::parse::<syn::Item>(input.clone()) let item = syn::parse::<syn::Item>(input.clone()).expect("expected a valid Rust item");
.expect("expected a valid Rust item"); let opts = syn::parse::<backend::ast::BindgenAttrs>(attr)
let opts = syn::parse::<ast::BindgenAttrs>(attr)
.expect("invalid arguments to #[wasm_bindgen]"); .expect("invalid arguments to #[wasm_bindgen]");
let mut ret = Tokens::new(); let mut ret = Tokens::new();
let mut program = ast::Program::default(); let mut program = backend::ast::Program::default();
program.push_item(item, Some(opts), &mut ret); program.push_item(item, Some(opts), &mut ret);
program.to_tokens(&mut ret); program.to_tokens(&mut ret);
@ -42,616 +26,3 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
ret.into() ret.into()
} }
fn to_ident_name(s: &str) -> Cow<str> {
if s.chars().all(|c| match c {
'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => true,
_ => false,
}) {
return Cow::from(s);
}
Cow::from(
s.chars()
.map(|c| match c {
'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => c,
_ => '_',
})
.collect::<String>(),
)
}
impl ToTokens for ast::Program {
// Generate wrappers for all the items that we've found
fn to_tokens(&self, tokens: &mut Tokens) {
for export in self.exports.iter() {
export.to_tokens(tokens);
}
for s in self.structs.iter() {
s.to_tokens(tokens);
}
let mut types = HashSet::new();
for i in self.imports.iter() {
if let ast::ImportKind::Type(ref t) = i.kind {
types.insert(t.name);
}
}
for i in self.imports.iter() {
match i.js_namespace {
Some(ns) if types.contains(&ns) => {
let kind = &i.kind;
(quote! { impl #ns { #kind } }).to_tokens(tokens);
}
_ => i.kind.to_tokens(tokens),
}
}
for e in self.enums.iter() {
e.to_tokens(tokens);
}
// Generate a static which will eventually be what lives in a custom section
// of the wasm executable. For now it's just a plain old static, but we'll
// eventually have it actually in its own section.
static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
let crate_name = env::var("CARGO_PKG_NAME").expect("should have CARGO_PKG_NAME env var");
let crate_vers = env::var("CARGO_PKG_VERSION").expect("should have CARGO_PKG_VERSION env var");
let generated_static_name = format!(
"__WASM_BINDGEN_GENERATED_{}_{}_{}",
to_ident_name(&crate_name),
to_ident_name(&crate_vers),
CNT.fetch_add(1, Ordering::SeqCst)
);
let generated_static_name = syn::Ident::from(generated_static_name);
let mut generated_static_value = Tokens::new();
let generated_static_length = self.literal(&mut generated_static_value);
(my_quote! {
#[allow(non_upper_case_globals)]
#[wasm_custom_section = "__wasm_bindgen_unstable"]
const #generated_static_name: [u8; #generated_static_length] =
[#generated_static_value];
}).to_tokens(tokens);
}
}
impl ToTokens for ast::Struct {
fn to_tokens(&self, tokens: &mut Tokens) {
let name = &self.name;
let free_fn = syn::Ident::from(shared::free_function(self.name.as_ref()));
let c = shared::name_to_descriptor(name.as_ref()) as u32;
(my_quote! {
impl ::wasm_bindgen::convert::WasmBoundary for #name {
type Js = u32;
const DESCRIPTOR: u32 = #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: u32) {
<#name as ::wasm_bindgen::convert::WasmBoundary>::from_js(ptr);
}
}).to_tokens(tokens);
}
}
impl ToTokens for ast::Export {
fn to_tokens(self: &ast::Export, into: &mut Tokens) {
let generated_name = self.rust_symbol();
let export_name = self.export_name();
let mut args = vec![];
let mut arg_conversions = vec![];
let mut converted_arguments = vec![];
let ret = syn::Ident::from("_ret");
let mut offset = 0;
if self.method {
let class = self.class.unwrap();
args.push(my_quote! { me: *mut ::wasm_bindgen::__rt::WasmRefCell<#class> });
arg_conversions.push(my_quote! {
::wasm_bindgen::__rt::assert_not_null(me);
let me = unsafe { &*me };
});
offset = 1;
}
for (i, ty) in self.function.arguments.iter().enumerate() {
let i = i + offset;
let ident = syn::Ident::from(format!("arg{}", i));
match *ty {
ast::Type::Vector(ref ty, owned) => {
let ptr = syn::Ident::from(format!("arg{}_ptr", i));
let len = syn::Ident::from(format!("arg{}_len", i));
let abi_ty = ty.abi_element();
args.push(my_quote! { #ptr: *mut #abi_ty });
args.push(my_quote! { #len: usize });
if owned {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::vec::Vec::from_raw_parts(#ptr, #len, #len)
};
});
} else {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::slice::from_raw_parts(#ptr as *const #abi_ty, #len)
};
});
}
if let ast::VectorType::String = *ty {
if owned {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::string::String::from_utf8_unchecked(#ident)
};
});
} else {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::str::from_utf8_unchecked(#ident)
};
});
}
}
}
ast::Type::ByValue(ref t) => {
args.push(my_quote! {
#ident: <#t as ::wasm_bindgen::convert::WasmBoundary >::Js
});
arg_conversions.push(my_quote! {
let #ident = unsafe {
<#t as ::wasm_bindgen::convert::WasmBoundary>
::from_js(#ident)
};
});
}
ast::Type::ByRef(ref ty) => {
args.push(my_quote! {
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
});
arg_conversions.push(my_quote! {
let #ident = unsafe {
<#ty as ::wasm_bindgen::convert::FromRefWasmBoundary>
::from_js_ref(#ident)
};
let #ident = &*#ident;
});
}
ast::Type::ByMutRef(ref ty) => {
args.push(my_quote! {
#ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js
});
arg_conversions.push(my_quote! {
let mut #ident = unsafe {
<#ty as ::wasm_bindgen::convert::FromRefMutWasmBoundary>
::from_js_ref_mut(#ident)
};
let #ident = &mut *#ident;
});
}
}
converted_arguments.push(my_quote! { #ident });
}
let ret_ty;
let convert_ret;
match self.function.ret {
Some(ast::Type::Vector(ref ty, true)) => {
ret_ty = my_quote! { -> *mut #ty };
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
}
Some(ast::Type::ByValue(ref t)) => {
ret_ty = my_quote! {
-> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
};
convert_ret = my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret)
};
}
Some(ast::Type::Vector(_, false)) |
Some(ast::Type::ByMutRef(_)) |
Some(ast::Type::ByRef(_)) => {
panic!("can't return a borrowed ref");
}
None => {
ret_ty = my_quote! {};
convert_ret = my_quote! {};
}
}
let name = self.function.name;
let receiver = match self.class {
Some(_) if self.method => {
if self.mutable {
my_quote! { me.borrow_mut().#name }
} else {
my_quote! { me.borrow().#name }
}
}
Some(class) => my_quote! { #class::#name },
None => my_quote!{ #name },
};
let tokens = my_quote! {
#[export_name = #export_name]
#[allow(non_snake_case)]
pub extern fn #generated_name(#(#args),*) #ret_ty {
::wasm_bindgen::__rt::link_this_library();
#(#arg_conversions)*
let #ret = #receiver(#(#converted_arguments),*);
#convert_ret
}
};
tokens.to_tokens(into);
}
}
impl ToTokens for ast::ImportType {
fn to_tokens(&self, tokens: &mut Tokens) {
let vis = &self.vis;
let name = &self.name;
(my_quote! {
#[allow(bad_style)]
#vis struct #name {
obj: ::wasm_bindgen::JsValue,
}
impl ::wasm_bindgen::convert::WasmBoundary for #name {
type Js = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::Js;
const DESCRIPTOR: u32 = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR;
fn into_js(self) -> Self::Js {
self.obj.into_js()
}
unsafe fn from_js(js: Self::Js) -> Self {
#name { obj: ::wasm_bindgen::JsValue::from_js(js) }
}
}
impl ::wasm_bindgen::convert::ToRefWasmBoundary for #name {
fn to_js_ref(&self) -> u32 {
self.obj.to_js_ref()
}
}
impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name {
type RefAnchor = ::std::mem::ManuallyDrop<#name>;
unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor {
let obj = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary>
::from_js(js);
::std::mem::ManuallyDrop::new(#name { obj })
}
}
impl From<::wasm_bindgen::JsValue> for #name {
fn from(obj: ::wasm_bindgen::JsValue) -> #name {
#name { obj }
}
}
impl From<#name> for ::wasm_bindgen::JsValue {
fn from(obj: #name) -> ::wasm_bindgen::JsValue {
obj.obj
}
}
}).to_tokens(tokens);
}
}
impl ToTokens for ast::ImportKind {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
ast::ImportKind::Function(ref f) => f.to_tokens(tokens),
ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
ast::ImportKind::Type(ref t) => t.to_tokens(tokens),
}
}
}
impl ToTokens for ast::ImportFunction {
fn to_tokens(&self, tokens: &mut Tokens) {
let mut class_ty = None;
let mut is_method = false;
match self.kind {
ast::ImportFunctionKind::Method { ref ty, .. } => {
is_method = true;
class_ty = Some(ty);
}
ast::ImportFunctionKind::JsConstructor { ref ty, .. } => {
class_ty = Some(ty);
}
ast::ImportFunctionKind::Normal => {}
}
let vis = &self.function.rust_vis;
let ret = &self.function.rust_decl.output;
let fn_token = &self.function.rust_decl.fn_token;
let mut abi_argument_names = Vec::new();
let mut abi_arguments = Vec::new();
let mut arg_conversions = Vec::new();
let ret_ident = syn::Ident::from("_ret");
let names = self.function.rust_decl.inputs
.iter()
.map(|arg| {
match *arg {
syn::FnArg::Captured(ref c) => c,
_ => panic!("arguments cannot be `self` or ignored"),
}
})
.map(|arg| {
match arg.pat {
syn::Pat::Ident(syn::PatIdent {
by_ref: None,
ident,
subpat: None,
..
}) => {
ident
}
_ => panic!("unsupported pattern in foreign function"),
}
});
for (i, (ty, name)) in self.function.arguments.iter().zip(names).enumerate() {
match *ty {
ast::Type::Vector(ref ty, owned) => {
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);
let abi_ty = ty.abi_element();
abi_arguments.push(my_quote! { #ptr: *const #abi_ty });
abi_arguments.push(my_quote! { #len: usize });
arg_conversions.push(my_quote! {
let #ptr = #name.as_ptr();
let #len = #name.len();
});
if owned {
arg_conversions.push(my_quote! { ::std::mem::forget(#name); });
}
}
ast::Type::ByValue(ref t) => {
abi_argument_names.push(name);
abi_arguments.push(my_quote! {
#name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
});
if i == 0 && is_method {
arg_conversions.push(my_quote! {
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
::into_js(self);
});
} else {
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 });
if i == 0 && is_method {
arg_conversions.push(my_quote! {
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
::to_js_ref(self);
});
} else {
arg_conversions.push(my_quote! {
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
::to_js_ref(#name);
});
}
}
}
}
let abi_ret;
let mut convert_ret;
match self.function.ret {
Some(ast::Type::ByValue(ref t)) => {
abi_ret = my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::Js
};
convert_ret = my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::from_js(#ret_ident)
};
}
Some(ast::Type::Vector(ref ty, true)) => {
let name = syn::Ident::from("__ret_len");
let name_ptr = syn::Ident::from("__ret_len_ptr");
abi_argument_names.push(name_ptr);
abi_arguments.push(my_quote! { #name_ptr: *mut usize });
arg_conversions.push(my_quote! {
let mut #name = 0;
let mut #name_ptr = &mut #name as *mut usize;
});
let abi_ty = ty.abi_element();
abi_ret = my_quote! { *mut #abi_ty };
if let ast::VectorType::String = *ty {
convert_ret = my_quote! {
String::from_utf8_unchecked(
Vec::from_raw_parts(#ret_ident, #name, #name)
)
};
} else {
convert_ret = my_quote! {
Vec::from_raw_parts(#ret_ident, #name, #name)
};
}
}
Some(ast::Type::ByRef(_)) |
Some(ast::Type::Vector(_, false)) |
Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
None => {
abi_ret = my_quote! { () };
convert_ret = my_quote! { () };
}
}
let mut exceptional_ret = my_quote! {};
if self.function.opts.catch() {
let exn_data = syn::Ident::from("exn_data");
let exn_data_ptr = syn::Ident::from("exn_data_ptr");
abi_argument_names.push(exn_data_ptr);
abi_arguments.push(my_quote! { #exn_data_ptr: *mut u32 });
arg_conversions.push(my_quote! {
let mut #exn_data = [0; 2];
let #exn_data_ptr = #exn_data.as_mut_ptr();
});
convert_ret = my_quote! { Ok(#convert_ret) };
exceptional_ret = my_quote! {
if #exn_data[0] == 1 {
return Err(<::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::from_js(#exn_data[1]))
}
};
}
let rust_name = self.rust_name;
let import_name = self.shim;
let attrs = &self.function.rust_attrs;
let arguments = self.function.rust_decl.inputs
.iter()
.skip(if is_method { 1 } else { 0 })
.collect::<Vec<_>>();
let me = if is_method {
my_quote! { &self, }
} else {
quote!()
};
let invocation = my_quote! {
#(#attrs)*
#[allow(bad_style)]
#vis extern #fn_token #rust_name(#me #(#arguments),*) #ret {
::wasm_bindgen::__rt::link_this_library();
#[wasm_import_module = "__wbindgen_placeholder__"]
extern {
fn #import_name(#(#abi_arguments),*) -> #abi_ret;
}
unsafe {
#(#arg_conversions)*
let #ret_ident = #import_name(#(#abi_argument_names),*);
#exceptional_ret
#convert_ret
}
}
};
if let Some(class) = class_ty {
(quote! {
impl #class {
#invocation
}
}).to_tokens(tokens);
} else {
invocation.to_tokens(tokens);
}
}
}
impl ToTokens for ast::Enum {
fn to_tokens(&self, into: &mut Tokens) {
let enum_name = &self.name;
let c = shared::TYPE_ENUM as u32;
let incoming_u32 = quote! { n };
let enum_name_as_string = enum_name.to_string();
let cast_clauses = self.variants.iter().map(|variant| {
let variant_name = &variant.name;
quote! {
if #incoming_u32 == #enum_name::#variant_name as u32 {
#enum_name::#variant_name
}
}
});
(my_quote! {
impl #enum_name {
fn from_u32(#incoming_u32: u32) -> #enum_name {
#(#cast_clauses else)* {
wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #enum_name_as_string));
}
}
}
impl ::wasm_bindgen::convert::WasmBoundary for #enum_name {
type Js = u32;
const DESCRIPTOR: u32 = #c;
fn into_js(self) -> u32 {
self as u32
}
unsafe fn from_js(js: u32) -> Self {
#enum_name::from_u32(js)
}
}
}).to_tokens(into);
}
}
impl ToTokens for ast::ImportStatic {
fn to_tokens(&self, into: &mut Tokens) {
let name = self.rust_name;
let ty = &self.ty;
let shim_name = self.shim;
let vis = &self.vis;
(my_quote! {
#[allow(bad_style)]
#vis static #name: ::wasm_bindgen::JsStatic<#ty> = {
fn init() -> #ty {
#[wasm_import_module = "__wbindgen_placeholder__"]
extern {
fn #shim_name() -> u32;
}
unsafe {
::wasm_bindgen::convert::WasmBoundary::from_js(#shim_name())
}
}
::wasm_bindgen::JsStatic {
__inner: ::std::cell::UnsafeCell::new(None),
__init: init,
}
};
}).to_tokens(into);
}
}