Start adding support for classes

This commit is contained in:
Alex Crichton
2017-12-18 12:39:14 -08:00
parent 2225942000
commit 7c510a8a7e
7 changed files with 504 additions and 171 deletions

View File

@ -1,9 +1,12 @@
use proc_macro2::Literal;
use quote::{ToTokens, Tokens};
use serde_json;
use syn;
use wasm_bindgen_shared as shared;
pub struct Program {
pub structs: Vec<Struct>,
pub free_functions: Vec<Function>,
}
pub struct Function {
pub name: syn::Ident,
pub arguments: Vec<Type>,
@ -14,6 +17,58 @@ pub enum Type {
Integer(syn::Ident),
BorrowedStr,
String,
ByValue(syn::Ident),
ByRef(syn::Ident),
ByMutRef(syn::Ident),
}
pub struct Struct {
pub name: syn::Ident,
pub ctor: Option<Function>,
pub methods: Vec<Method>,
pub functions: Vec<Function>,
}
pub struct Method {
pub mutable: bool,
pub function: Function,
}
impl Program {
pub fn push_impl(&mut self, item: &syn::ItemImpl) {
match item.defaultness {
syn::Defaultness::Final => {}
_ => panic!("default impls are not supported"),
}
match item.unsafety {
syn::Unsafety::Normal => {}
_ => panic!("unsafe impls are not supported"),
}
if item.trait_.is_some() {
panic!("trait impls are not supported");
}
if item.generics.params.len() > 0 {
panic!("generic impls aren't supported");
}
let name = match Type::from(&item.self_ty) {
Type::ByValue(ident) => ident,
_ => panic!("unsupported self type in impl"),
};
let dst = self.structs
.iter_mut()
.find(|s| s.name == name)
.expect(&format!("failed to definition of struct for impl of `{}`", name));
for item in item.items.iter() {
dst.push_item(item);
}
}
pub fn shared(&self) -> shared::Program {
shared::Program {
structs: self.structs.iter().map(|s| s.shared()).collect(),
free_functions: self.free_functions.iter().map(|s| s.shared()).collect(),
}
}
}
impl Function {
@ -36,6 +91,7 @@ impl Function {
if !input.abi.is_none() {
panic!("can only bindgen Rust ABI functions")
}
if input.decl.variadic {
panic!("can't bindgen variadic functions")
}
@ -59,11 +115,7 @@ impl Function {
syn::ReturnType::Type(ref t, _) => Some(Type::from(t)),
};
Function {
name: input.ident,
arguments,
ret,
}
Function { name: input.ident, arguments, ret }
}
pub fn export_name(&self) -> syn::Lit {
@ -79,18 +131,6 @@ impl Function {
syn::Ident::from(generated_name)
}
pub fn generated_static_name(&self) -> syn::Ident {
let generated_name = format!("__WASM_BINDGEN_GENERATED_{}",
self.name.sym.as_str());
syn::Ident::from(generated_name)
}
pub fn generate_static(&self) -> Vec<u8> {
let mut prefix = String::from("wbg:");
prefix.push_str(&serde_json::to_string(&self.shared()).unwrap());
prefix.into_bytes()
}
fn shared(&self) -> shared::Function {
shared::Function {
name: self.name.sym.as_str().to_string(),
@ -120,16 +160,22 @@ impl Type {
if r.lifetime.is_some() {
panic!("can't have lifetimes on references yet");
}
match r.ty.mutability {
syn::Mutability::Immutable => {}
_ => panic!("can't have mutable references yet"),
}
let mutable = match r.ty.mutability {
syn::Mutability::Immutable => false,
syn::Mutability::Mutable(_) => true,
};
match r.ty.ty {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
let ident = extract_path_ident(path);
match ident.sym.as_str() {
"str" => Type::BorrowedStr,
_ => panic!("unsupported reference type"),
"str" => {
if mutable {
panic!("mutable strings not allowed");
}
Type::BorrowedStr
}
_ if mutable => Type::ByMutRef(ident),
_ => Type::ByRef(ident),
}
}
_ => panic!("unsupported reference type"),
@ -151,7 +197,7 @@ impl Type {
Type::Integer(ident)
}
"String" => Type::String,
s => panic!("unsupported type: {}", s),
_ => Type::ByValue(ident),
}
}
_ => panic!("unsupported type"),
@ -163,21 +209,108 @@ impl Type {
Type::Integer(_) => shared::Type::Number,
Type::BorrowedStr => shared::Type::BorrowedStr,
Type::String => shared::Type::String,
Type::ByValue(n) => shared::Type::ByValue(n.to_string()),
Type::ByRef(n) => shared::Type::ByRef(n.to_string()),
Type::ByMutRef(n) => shared::Type::ByMutRef(n.to_string()),
}
}
}
impl ToTokens for Type {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
Type::Integer(i) => i.to_tokens(tokens),
Type::String => {
syn::Ident::from("String").to_tokens(tokens);
impl Struct {
pub fn from(s: &syn::ItemStruct) -> Struct {
Struct {
name: s.ident,
ctor: None,
methods: Vec::new(),
functions: Vec::new(),
}
}
pub fn push_item(&mut self, item: &syn::ImplItem) {
let method = match *item {
syn::ImplItem::Const(_) => panic!("const definitions aren't supported"),
syn::ImplItem::Type(_) => panic!("type definitions in impls aren't supported"),
syn::ImplItem::Method(ref m) => m,
syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"),
};
match method.vis {
syn::Visibility::Public(_) => {}
_ => return,
}
match method.defaultness {
syn::Defaultness::Final => {}
_ => panic!("default methods are not supported"),
}
match method.sig.constness {
syn::Constness::NotConst => {}
_ => panic!("can only bindgen non-const functions"),
}
match method.sig.unsafety {
syn::Unsafety::Normal => {}
_ => panic!("can only bindgen safe functions"),
}
if method.sig.decl.variadic {
panic!("can't bindgen variadic functions")
}
if method.sig.decl.generics.params.len() > 0 {
panic!("can't bindgen functions with lifetime or type parameters")
}
let mut mutable = None;
let arguments = method.sig.decl.inputs.iter()
.map(|i| i.into_item())
.filter_map(|arg| {
match *arg {
syn::FnArg::Captured(ref c) => Some(c),
syn::FnArg::SelfValue(_) => {
panic!("by-value `self` not yet supported");
}
syn::FnArg::SelfRef(ref a) => {
assert!(mutable.is_none());
mutable = Some(match a.mutbl {
syn::Mutability::Mutable(_) => true,
syn::Mutability::Immutable => false,
});
None
}
_ => panic!("arguments cannot be `self` or ignored"),
}
})
.map(|arg| Type::from(&arg.ty))
.collect::<Vec<_>>();
let ret = match method.sig.decl.output {
syn::ReturnType::Default => None,
syn::ReturnType::Type(ref t, _) => Some(Type::from(t)),
};
let function = Function { name: method.sig.ident, arguments, ret };
match mutable {
Some(mutable) => {
self.methods.push(Method { mutable, function });
}
Type::BorrowedStr => {
<Token![&]>::default().to_tokens(tokens);
syn::Ident::from("str").to_tokens(tokens);
None => {
self.functions.push(function);
}
}
}
pub fn shared(&self) -> shared::Struct {
shared::Struct {
name: self.name.to_string(),
ctor: self.ctor.as_ref().unwrap().shared(),
functions: self.functions.iter().map(|f| f.shared()).collect(),
methods: self.methods.iter().map(|f| f.shared()).collect(),
}
}
}
impl Method {
pub fn shared(&self) -> shared::Method {
shared::Method {
mutable: self.mutable,
function: self.function.shared(),
}
}
}

View File

@ -1,7 +1,6 @@
#![feature(proc_macro)]
extern crate syn;
#[macro_use]
extern crate synom;
#[macro_use]
extern crate quote;
@ -28,25 +27,63 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
let mut ret = Tokens::new();
let mut program = ast::Program {
structs: Vec::new(),
free_functions: Vec::new(),
};
for item in file.items.iter() {
item.to_tokens(&mut ret);
match *item {
syn::Item::Fn(ref f) => bindgen_fn(f, &mut ret),
syn::Item::Fn(ref f) => {
program.free_functions.push(ast::Function::from(f));
}
syn::Item::Struct(ref s) => {
let s = ast::Struct::from(s);
if program.structs.iter().any(|a| a.name == s.name) {
panic!("redefinition of struct: {}", s.name);
}
program.structs.push(s);
}
syn::Item::Impl(ref s) => program.push_impl(s),
_ => panic!("unexpected item in bindgen macro"),
}
}
for function in program.free_functions.iter() {
bindgen_fn(function, &mut ret);
}
for s in program.structs.iter() {
bindgen_struct(s, &mut ret);
}
static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
let generated_static_name = format!("__WASM_BINDGEN_GENERATED{}",
CNT.fetch_add(1, Ordering::SeqCst));
let mut generated_static = String::from("wbg:");
generated_static.push_str(&serde_json::to_string(&program.shared()).unwrap());
let generated_static_value = syn::Lit {
value: syn::LitKind::Other(Literal::byte_string(generated_static.as_bytes())),
span: Default::default(),
};
let generated_static_length = generated_static.len();
(quote! {
#[no_mangle]
#[allow(non_upper_case_globals)]
pub static #generated_static_name: [u8; #generated_static_length] =
*#generated_static_value;
}).to_tokens(&mut ret);
ret.into()
}
fn bindgen_fn(input: &syn::ItemFn, into: &mut Tokens) {
let function = ast::Function::from(input);
fn bindgen_fn(function: &ast::Function, into: &mut Tokens) {
let export_name = function.export_name();
let generated_name = function.rust_symbol();
let mut args = vec![];
let mut arg_conversions = vec![];
let real_name = &input.ident;
let real_name = &function.name;
let mut converted_arguments = vec![];
let ret = syn::Ident::from("_ret");
@ -85,6 +122,32 @@ fn bindgen_fn(input: &syn::ItemFn, into: &mut Tokens) {
};
});
}
ast::Type::ByValue(name) => {
args.push(quote! { #ident: *mut ::std::cell::RefCell<#name> });
arg_conversions.push(quote! {
assert!(!#ident.is_null());
let #ident = unsafe {
(*#ident).borrow_mut();
Box::from_raw(#ident).into_inner()
};
});
}
ast::Type::ByRef(name) => {
args.push(quote! { #ident: *mut ::std::cell::RefCell<#name> });
arg_conversions.push(quote! {
assert!(!#ident.is_null());
let #ident = unsafe { (*#ident).borrow() };
let #ident = &*#ident;
});
}
ast::Type::ByMutRef(name) => {
args.push(quote! { #ident: *mut ::std::cell::RefCell<#name> });
arg_conversions.push(quote! {
assert!(!#ident.is_null());
let mut #ident = unsafe { (*#ident).borrow_mut() };
let #ident = &mut *#ident;
});
}
}
converted_arguments.push(quote! { #ident });
}
@ -96,25 +159,25 @@ fn bindgen_fn(input: &syn::ItemFn, into: &mut Tokens) {
convert_ret = quote! { #ret };
}
Some(ast::Type::BorrowedStr) => panic!("can't return a borrowed string"),
Some(ast::Type::ByRef(_)) => panic!("can't return a borrowed ref"),
Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
Some(ast::Type::String) => {
boxed_str = !BOXED_STR_GENERATED.swap(true, Ordering::SeqCst);
ret_ty = quote! { -> *mut String };
convert_ret = quote! { Box::into_raw(Box::new(#ret)) };
}
Some(ast::Type::ByValue(name)) => {
ret_ty = quote! { -> *mut ::std::cell::RefCell<#name> };
convert_ret = quote! {
Box::into_raw(Box::new(::std::cell::RefCell<#ret>))
};
}
None => {
ret_ty = quote! {};
convert_ret = quote! {};
}
}
let generated_static_name = function.generated_static_name();
let generated_static = function.generate_static();
let generated_static_value = syn::Lit {
value: syn::LitKind::Other(Literal::byte_string(&generated_static)),
span: Default::default(),
};
let generated_static_length = generated_static.len();
let malloc = if malloc {
quote! {
#[no_mangle]
@ -161,11 +224,6 @@ fn bindgen_fn(input: &syn::ItemFn, into: &mut Tokens) {
#malloc
#boxed_str
#[no_mangle]
#[allow(non_upper_case_globals)]
pub static #generated_static_name: [u8; #generated_static_length] =
*#generated_static_value;
#[no_mangle]
#[export_name = #export_name]
pub extern fn #generated_name(#(#args),*) #ret_ty {
@ -177,3 +235,9 @@ fn bindgen_fn(input: &syn::ItemFn, into: &mut Tokens) {
// println!("{}", tokens);
tokens.to_tokens(into);
}
fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
if s.ctor.is_none() {
panic!("struct `{}` needs a `new` function to construct it", s.name);
}
}