mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-14 13:31:22 +00:00
Initial commit
This commit is contained in:
151
crates/wasm-bindgen-macro/src/ast.rs
Normal file
151
crates/wasm-bindgen-macro/src/ast.rs
Normal file
@ -0,0 +1,151 @@
|
||||
use proc_macro2::Literal;
|
||||
use quote::{ToTokens, Tokens};
|
||||
use serde_json;
|
||||
use syn;
|
||||
use wasm_bindgen_shared as shared;
|
||||
|
||||
pub struct Function {
|
||||
pub name: syn::Ident,
|
||||
pub arguments: Vec<Type>,
|
||||
pub ret: Option<Type>,
|
||||
}
|
||||
|
||||
pub enum Type {
|
||||
Integer(syn::Ident),
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn from(input: &syn::ItemFn) -> Function {
|
||||
match input.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => panic!("can only bindgen public functions"),
|
||||
}
|
||||
match input.constness {
|
||||
syn::Constness::NotConst => {}
|
||||
_ => panic!("can only bindgen non-const functions"),
|
||||
}
|
||||
match input.unsafety {
|
||||
syn::Unsafety::Normal => {}
|
||||
_ => panic!("can only bindgen safe functions"),
|
||||
}
|
||||
if !input.abi.is_none() {
|
||||
panic!("can only bindgen Rust ABI functions")
|
||||
}
|
||||
if !input.abi.is_none() {
|
||||
panic!("can only bindgen Rust ABI functions")
|
||||
}
|
||||
if input.decl.variadic {
|
||||
panic!("can't bindgen variadic functions")
|
||||
}
|
||||
if input.decl.generics.params.len() > 0 {
|
||||
panic!("can't bindgen functions with lifetime or type parameters")
|
||||
}
|
||||
|
||||
let arguments = input.decl.inputs.iter()
|
||||
.map(|i| i.into_item())
|
||||
.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<_>>();
|
||||
|
||||
let ret = match input.decl.output {
|
||||
syn::ReturnType::Default => None,
|
||||
syn::ReturnType::Type(ref t, _) => Some(Type::from(t)),
|
||||
};
|
||||
|
||||
Function {
|
||||
name: input.ident,
|
||||
arguments,
|
||||
ret,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export_name(&self) -> syn::Lit {
|
||||
syn::Lit {
|
||||
value: syn::LitKind::Other(Literal::string(self.name.sym.as_str())),
|
||||
span: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rust_symbol(&self) -> syn::Ident {
|
||||
let generated_name = format!("__wasm_bindgen_generated_{}",
|
||||
self.name.sym.as_str());
|
||||
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(),
|
||||
arguments: self.arguments.iter().map(|t| t.shared()).collect(),
|
||||
ret: self.ret.as_ref().map(|t| t.shared()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn from(ty: &syn::Type) -> Type {
|
||||
match *ty {
|
||||
// syn::Type::Reference(ref r) => {
|
||||
// }
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
|
||||
if path.leading_colon.is_some() {
|
||||
panic!("unsupported leading colon in path")
|
||||
}
|
||||
if path.segments.len() != 1 {
|
||||
panic!("unsupported path that needs name resolution")
|
||||
}
|
||||
match path.segments.get(0).item().arguments {
|
||||
syn::PathArguments::None => {}
|
||||
_ => panic!("unsupported path that has path arguments")
|
||||
}
|
||||
let ident = path.segments.get(0).item().ident;
|
||||
match ident.sym.as_str() {
|
||||
"i8" |
|
||||
"u8" |
|
||||
"u16" |
|
||||
"i16" |
|
||||
"u32" |
|
||||
"i32" |
|
||||
"isize" |
|
||||
"usize" |
|
||||
"f32" |
|
||||
"f64" => {
|
||||
Type::Integer(ident)
|
||||
}
|
||||
s => panic!("unsupported type: {}", s),
|
||||
}
|
||||
}
|
||||
_ => panic!("unsupported type"),
|
||||
}
|
||||
}
|
||||
|
||||
fn shared(&self) -> shared::Type {
|
||||
match *self {
|
||||
Type::Integer(_) => shared::Type::Number,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Type {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
match *self {
|
||||
Type::Integer(i) => i.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
93
crates/wasm-bindgen-macro/src/lib.rs
Normal file
93
crates/wasm-bindgen-macro/src/lib.rs
Normal file
@ -0,0 +1,93 @@
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
extern crate serde_json;
|
||||
extern crate wasm_bindgen_shared;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Literal;
|
||||
use quote::{Tokens, ToTokens};
|
||||
|
||||
mod ast;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
let file = syn::parse::<syn::File>(input)
|
||||
.expect("expected a set of valid Rust items");
|
||||
|
||||
|
||||
let mut ret = Tokens::new();
|
||||
|
||||
for item in file.items.iter() {
|
||||
item.to_tokens(&mut ret);
|
||||
match *item {
|
||||
syn::Item::Fn(ref f) => bindgen_fn(f, &mut ret),
|
||||
_ => panic!("unexpected item in bindgen macro"),
|
||||
}
|
||||
}
|
||||
|
||||
ret.into()
|
||||
}
|
||||
|
||||
fn bindgen_fn(input: &syn::ItemFn, into: &mut Tokens) {
|
||||
let function = ast::Function::from(input);
|
||||
|
||||
let export_name = function.export_name();
|
||||
let generated_name = function.rust_symbol();
|
||||
let mut args = vec![];
|
||||
let arg_conversions = vec![quote!{}];
|
||||
let real_name = &input.ident;
|
||||
let mut converted_arguments = vec![];
|
||||
let ret = syn::Ident::from("_ret");
|
||||
|
||||
for (i, ty) in function.arguments.iter().enumerate() {
|
||||
let ident = syn::Ident::from(format!("arg{}", i));
|
||||
match *ty {
|
||||
ast::Type::Integer(i) => {
|
||||
args.push(quote! { #ident: #i });
|
||||
converted_arguments.push(quote! { #ident });
|
||||
}
|
||||
}
|
||||
}
|
||||
let ret_ty;
|
||||
let convert_ret;
|
||||
match function.ret {
|
||||
Some(ast::Type::Integer(i)) => {
|
||||
ret_ty = quote! { -> #i };
|
||||
convert_ret = quote! { #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 tokens = quote! {
|
||||
#[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 {
|
||||
#(#arg_conversions);*
|
||||
let #ret = #real_name(#(#converted_arguments),*);
|
||||
#convert_ret
|
||||
}
|
||||
};
|
||||
tokens.to_tokens(into);
|
||||
}
|
Reference in New Issue
Block a user