2018-01-08 10:42:01 -08:00
|
|
|
use proc_macro2::Span;
|
2017-12-14 19:31:01 -08:00
|
|
|
use syn;
|
|
|
|
use wasm_bindgen_shared as shared;
|
|
|
|
|
2017-12-18 12:39:14 -08:00
|
|
|
pub struct Program {
|
|
|
|
pub structs: Vec<Struct>,
|
|
|
|
pub free_functions: Vec<Function>,
|
2017-12-18 21:43:16 -08:00
|
|
|
pub imports: Vec<Import>,
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
|
|
|
|
2017-12-14 19:31:01 -08:00
|
|
|
pub struct Function {
|
|
|
|
pub name: syn::Ident,
|
|
|
|
pub arguments: Vec<Type>,
|
|
|
|
pub ret: Option<Type>,
|
|
|
|
}
|
|
|
|
|
2017-12-18 21:43:16 -08:00
|
|
|
pub struct Import {
|
|
|
|
pub function: Function,
|
|
|
|
pub decl: Box<syn::FnDecl>,
|
|
|
|
pub ident: syn::Ident,
|
|
|
|
pub vis: syn::Visibility,
|
|
|
|
pub attrs: Vec<syn::Attribute>,
|
|
|
|
}
|
|
|
|
|
2017-12-14 19:31:01 -08:00
|
|
|
pub enum Type {
|
|
|
|
Integer(syn::Ident),
|
2017-12-14 21:55:21 -08:00
|
|
|
BorrowedStr,
|
|
|
|
String,
|
2017-12-18 12:39:14 -08:00
|
|
|
ByValue(syn::Ident),
|
|
|
|
ByRef(syn::Ident),
|
|
|
|
ByMutRef(syn::Ident),
|
2017-12-20 07:35:14 -08:00
|
|
|
RawMutPtr(syn::Ident),
|
|
|
|
RawConstPtr(syn::Ident),
|
2017-12-19 09:25:41 -08:00
|
|
|
JsObject,
|
|
|
|
JsObjectRef,
|
2017-12-20 10:22:18 -08:00
|
|
|
Boolean,
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Struct {
|
|
|
|
pub name: syn::Ident,
|
|
|
|
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) {
|
2017-12-31 14:40:57 -08:00
|
|
|
if item.defaultness.is_some() {
|
|
|
|
panic!("default impls are not supported");
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
2017-12-31 14:40:57 -08:00
|
|
|
if item.unsafety.is_some() {
|
|
|
|
panic!("unsafe impls are not supported");
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 21:43:16 -08:00
|
|
|
pub fn push_foreign_mod(&mut self, f: &syn::ItemForeignMod) {
|
2017-12-31 14:40:57 -08:00
|
|
|
match f.abi.name {
|
2018-01-08 10:42:01 -08:00
|
|
|
Some(ref l) if l.value() == "JS" => {}
|
2017-12-18 21:43:16 -08:00
|
|
|
_ => panic!("only foreign mods with the `JS` ABI are allowed"),
|
|
|
|
}
|
|
|
|
for item in f.items.iter() {
|
|
|
|
self.push_foreign_item(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn push_foreign_item(&mut self, f: &syn::ForeignItem) {
|
|
|
|
let f = match *f {
|
|
|
|
syn::ForeignItem::Fn(ref f) => f,
|
|
|
|
_ => panic!("only foreign functions allowed for now, not statics"),
|
|
|
|
};
|
|
|
|
|
|
|
|
self.imports.push(Import {
|
|
|
|
attrs: f.attrs.clone(),
|
|
|
|
vis: f.vis.clone(),
|
|
|
|
decl: f.decl.clone(),
|
|
|
|
ident: f.ident.clone(),
|
|
|
|
function: Function::from_decl(f.ident, &f.decl),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-18 12:39:14 -08:00
|
|
|
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(),
|
2017-12-18 21:43:16 -08:00
|
|
|
imports: self.imports.iter().map(|i| i.function.shared()).collect(),
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
|
|
|
}
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Function {
|
|
|
|
pub fn from(input: &syn::ItemFn) -> Function {
|
|
|
|
match input.vis {
|
|
|
|
syn::Visibility::Public(_) => {}
|
|
|
|
_ => panic!("can only bindgen public functions"),
|
|
|
|
}
|
2017-12-31 14:40:57 -08:00
|
|
|
if input.constness.is_some() {
|
|
|
|
panic!("can only bindgen non-const functions");
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
2017-12-31 14:40:57 -08:00
|
|
|
if input.unsafety.is_some() {
|
|
|
|
panic!("can only bindgen safe functions");
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
|
|
|
if !input.abi.is_none() {
|
|
|
|
panic!("can only bindgen Rust ABI functions")
|
|
|
|
}
|
|
|
|
if !input.abi.is_none() {
|
|
|
|
panic!("can only bindgen Rust ABI functions")
|
|
|
|
}
|
2017-12-18 12:39:14 -08:00
|
|
|
|
2017-12-18 21:43:16 -08:00
|
|
|
Function::from_decl(input.ident, &input.decl)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_decl(name: syn::Ident, decl: &syn::FnDecl) -> Function {
|
2017-12-31 14:40:57 -08:00
|
|
|
if decl.variadic.is_some() {
|
2017-12-14 19:31:01 -08:00
|
|
|
panic!("can't bindgen variadic functions")
|
|
|
|
}
|
2017-12-18 21:43:16 -08:00
|
|
|
if decl.generics.params.len() > 0 {
|
2017-12-14 19:31:01 -08:00
|
|
|
panic!("can't bindgen functions with lifetime or type parameters")
|
|
|
|
}
|
|
|
|
|
2017-12-18 21:43:16 -08:00
|
|
|
let arguments = decl.inputs.iter()
|
2017-12-14 19:31:01 -08:00
|
|
|
.map(|arg| {
|
|
|
|
match *arg {
|
|
|
|
syn::FnArg::Captured(ref c) => c,
|
|
|
|
_ => panic!("arguments cannot be `self` or ignored"),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.map(|arg| Type::from(&arg.ty))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2017-12-18 21:43:16 -08:00
|
|
|
let ret = match decl.output {
|
2017-12-14 19:31:01 -08:00
|
|
|
syn::ReturnType::Default => None,
|
2017-12-31 14:40:57 -08:00
|
|
|
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
2017-12-14 19:31:01 -08:00
|
|
|
};
|
|
|
|
|
2017-12-18 21:43:16 -08:00
|
|
|
Function { name, arguments, ret }
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
|
|
|
|
2018-01-08 10:42:01 -08:00
|
|
|
pub fn free_function_export_name(&self) -> syn::LitStr {
|
2017-12-18 14:31:01 -08:00
|
|
|
let name = self.shared().free_function_export_name();
|
2018-01-08 10:42:01 -08:00
|
|
|
syn::LitStr::new(&name, Span::def_site())
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
|
|
|
|
2018-01-08 10:42:01 -08:00
|
|
|
pub fn struct_function_export_name(&self, s: syn::Ident) -> syn::LitStr {
|
2017-12-31 14:40:57 -08:00
|
|
|
let name = self.shared().struct_function_export_name(s.as_ref());
|
2018-01-08 10:42:01 -08:00
|
|
|
syn::LitStr::new(&name, Span::def_site())
|
2017-12-18 14:31:01 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rust_symbol(&self, namespace: Option<syn::Ident>) -> syn::Ident {
|
|
|
|
let mut generated_name = format!("__wasm_bindgen_generated");
|
|
|
|
if let Some(ns) = namespace {
|
|
|
|
generated_name.push_str("_");
|
2017-12-31 14:40:57 -08:00
|
|
|
generated_name.push_str(ns.as_ref());
|
2017-12-18 14:31:01 -08:00
|
|
|
}
|
|
|
|
generated_name.push_str("_");
|
2017-12-31 14:40:57 -08:00
|
|
|
generated_name.push_str(self.name.as_ref());
|
2017-12-14 19:31:01 -08:00
|
|
|
syn::Ident::from(generated_name)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn shared(&self) -> shared::Function {
|
|
|
|
shared::Function {
|
2017-12-31 14:40:57 -08:00
|
|
|
name: self.name.as_ref().to_string(),
|
2017-12-14 19:31:01 -08:00
|
|
|
arguments: self.arguments.iter().map(|t| t.shared()).collect(),
|
|
|
|
ret: self.ret.as_ref().map(|t| t.shared()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 21:43:16 -08:00
|
|
|
pub fn extract_path_ident(path: &syn::Path) -> syn::Ident {
|
|
|
|
if path.leading_colon.is_some() {
|
|
|
|
panic!("unsupported leading colon in path")
|
|
|
|
}
|
|
|
|
if path.segments.len() != 1 {
|
|
|
|
panic!("unsupported path that needs name resolution")
|
|
|
|
}
|
2018-01-08 10:42:01 -08:00
|
|
|
match path.segments.first().unwrap().value().arguments {
|
2017-12-18 21:43:16 -08:00
|
|
|
syn::PathArguments::None => {}
|
|
|
|
_ => panic!("unsupported path that has path arguments")
|
|
|
|
}
|
2018-01-08 10:42:01 -08:00
|
|
|
path.segments.first().unwrap().value().ident
|
2017-12-18 21:43:16 -08:00
|
|
|
}
|
|
|
|
|
2017-12-14 19:31:01 -08:00
|
|
|
impl Type {
|
|
|
|
pub fn from(ty: &syn::Type) -> Type {
|
|
|
|
match *ty {
|
2017-12-14 21:55:21 -08:00
|
|
|
syn::Type::Reference(ref r) => {
|
|
|
|
if r.lifetime.is_some() {
|
|
|
|
panic!("can't have lifetimes on references yet");
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
2017-12-31 14:40:57 -08:00
|
|
|
let mutable = r.mutability.is_some();
|
|
|
|
match *r.elem {
|
2017-12-14 21:55:21 -08:00
|
|
|
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
|
|
|
let ident = extract_path_ident(path);
|
2017-12-31 14:40:57 -08:00
|
|
|
match ident.as_ref() {
|
2017-12-18 12:39:14 -08:00
|
|
|
"str" => {
|
|
|
|
if mutable {
|
|
|
|
panic!("mutable strings not allowed");
|
|
|
|
}
|
|
|
|
Type::BorrowedStr
|
|
|
|
}
|
2017-12-19 09:25:41 -08:00
|
|
|
"JsObject" if !mutable => Type::JsObjectRef,
|
|
|
|
"JsObject" if mutable => {
|
|
|
|
panic!("can't have mutable js object refs")
|
|
|
|
}
|
2017-12-18 12:39:14 -08:00
|
|
|
_ if mutable => Type::ByMutRef(ident),
|
|
|
|
_ => Type::ByRef(ident),
|
2017-12-14 21:55:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => panic!("unsupported reference type"),
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
2017-12-14 21:55:21 -08:00
|
|
|
}
|
2017-12-20 07:35:14 -08:00
|
|
|
syn::Type::Ptr(ref p) => {
|
|
|
|
let mutable = p.const_token.is_none();
|
2017-12-31 14:40:57 -08:00
|
|
|
let ident = match *p.elem {
|
2017-12-20 07:35:14 -08:00
|
|
|
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
|
|
|
extract_path_ident(path)
|
|
|
|
}
|
|
|
|
_ => panic!("unsupported reference type"),
|
|
|
|
};
|
|
|
|
if mutable {
|
|
|
|
Type::RawMutPtr(ident)
|
|
|
|
} else {
|
|
|
|
Type::RawConstPtr(ident)
|
|
|
|
}
|
|
|
|
}
|
2017-12-14 21:55:21 -08:00
|
|
|
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
|
|
|
let ident = extract_path_ident(path);
|
2017-12-31 14:40:57 -08:00
|
|
|
match ident.as_ref() {
|
2017-12-14 19:31:01 -08:00
|
|
|
"i8" |
|
|
|
|
"u8" |
|
|
|
|
"u16" |
|
|
|
|
"i16" |
|
|
|
|
"u32" |
|
|
|
|
"i32" |
|
|
|
|
"isize" |
|
|
|
|
"usize" |
|
|
|
|
"f32" |
|
|
|
|
"f64" => {
|
|
|
|
Type::Integer(ident)
|
|
|
|
}
|
2017-12-20 10:22:18 -08:00
|
|
|
"bool" => Type::Boolean,
|
2017-12-14 21:55:21 -08:00
|
|
|
"String" => Type::String,
|
2017-12-19 09:25:41 -08:00
|
|
|
"JsObject" => Type::JsObject,
|
2017-12-18 12:39:14 -08:00
|
|
|
_ => Type::ByValue(ident),
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => panic!("unsupported type"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn shared(&self) -> shared::Type {
|
|
|
|
match *self {
|
2017-12-20 07:35:14 -08:00
|
|
|
Type::Integer(_) |
|
|
|
|
Type::RawConstPtr(_) |
|
|
|
|
Type::RawMutPtr(_) => shared::Type::Number,
|
2017-12-14 21:55:21 -08:00
|
|
|
Type::BorrowedStr => shared::Type::BorrowedStr,
|
|
|
|
Type::String => shared::Type::String,
|
2017-12-18 12:39:14 -08:00
|
|
|
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()),
|
2017-12-19 09:25:41 -08:00
|
|
|
Type::JsObject => shared::Type::JsObject,
|
|
|
|
Type::JsObjectRef => shared::Type::JsObjectRef,
|
2017-12-20 10:22:18 -08:00
|
|
|
Type::Boolean => shared::Type::Boolean,
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 12:39:14 -08:00
|
|
|
impl Struct {
|
|
|
|
pub fn from(s: &syn::ItemStruct) -> Struct {
|
|
|
|
Struct {
|
|
|
|
name: s.ident,
|
|
|
|
methods: Vec::new(),
|
|
|
|
functions: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 14:31:01 -08:00
|
|
|
pub fn free_function(&self) -> syn::Ident {
|
|
|
|
syn::Ident::from(self.shared().free_function())
|
|
|
|
}
|
|
|
|
|
2017-12-18 12:39:14 -08:00
|
|
|
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"),
|
2017-12-31 14:40:57 -08:00
|
|
|
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
2017-12-18 12:39:14 -08:00
|
|
|
};
|
|
|
|
match method.vis {
|
|
|
|
syn::Visibility::Public(_) => {}
|
|
|
|
_ => return,
|
|
|
|
}
|
2017-12-31 14:40:57 -08:00
|
|
|
if method.defaultness.is_some() {
|
|
|
|
panic!("default methods are not supported");
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
2017-12-31 14:40:57 -08:00
|
|
|
if method.sig.constness.is_some() {
|
|
|
|
panic!("can only bindgen non-const functions");
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
2017-12-31 14:40:57 -08:00
|
|
|
if method.sig.unsafety.is_some() {
|
|
|
|
panic!("can only bindgen safe functions");
|
2017-12-18 12:39:14 -08:00
|
|
|
}
|
|
|
|
|
2017-12-31 14:40:57 -08:00
|
|
|
if method.sig.decl.variadic.is_some() {
|
2017-12-18 12:39:14 -08:00
|
|
|
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()
|
|
|
|
.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());
|
2017-12-31 14:40:57 -08:00
|
|
|
mutable = Some(a.mutability.is_some());
|
2017-12-18 12:39:14 -08:00
|
|
|
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,
|
2017-12-31 14:40:57 -08:00
|
|
|
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
2017-12-18 12:39:14 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
let function = Function { name: method.sig.ident, arguments, ret };
|
|
|
|
match mutable {
|
|
|
|
Some(mutable) => {
|
|
|
|
self.methods.push(Method { mutable, function });
|
2017-12-14 21:55:21 -08:00
|
|
|
}
|
2017-12-18 12:39:14 -08:00
|
|
|
None => {
|
|
|
|
self.functions.push(function);
|
2017-12-14 21:55:21 -08:00
|
|
|
}
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|
|
|
|
}
|
2017-12-18 12:39:14 -08:00
|
|
|
|
|
|
|
pub fn shared(&self) -> shared::Struct {
|
|
|
|
shared::Struct {
|
|
|
|
name: self.name.to_string(),
|
|
|
|
functions: self.functions.iter().map(|f| f.shared()).collect(),
|
|
|
|
methods: self.methods.iter().map(|f| f.shared()).collect(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Method {
|
|
|
|
pub fn shared(&self) -> shared::Method {
|
|
|
|
shared::Method {
|
|
|
|
mutable: self.mutable,
|
|
|
|
function: self.function.shared(),
|
|
|
|
}
|
|
|
|
}
|
2017-12-14 19:31:01 -08:00
|
|
|
}
|