mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-12 12:31:22 +00:00
Split out the AST and codegen into the wasm-bindgen-backend crate
This commit is contained in:
889
crates/backend/src/ast.rs
Normal file
889
crates/backend/src/ast.rs
Normal file
@ -0,0 +1,889 @@
|
||||
use literal::{self, Literal};
|
||||
use proc_macro2::Span;
|
||||
use quote::{ToTokens, Tokens};
|
||||
use shared;
|
||||
use syn;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Program {
|
||||
pub exports: Vec<Export>,
|
||||
pub imports: Vec<Import>,
|
||||
pub enums: Vec<Enum>,
|
||||
pub structs: Vec<Struct>,
|
||||
}
|
||||
|
||||
pub struct Export {
|
||||
pub class: Option<syn::Ident>,
|
||||
pub method: bool,
|
||||
pub mutable: bool,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
pub struct Import {
|
||||
pub module: Option<String>,
|
||||
pub js_namespace: Option<syn::Ident>,
|
||||
pub kind: ImportKind,
|
||||
}
|
||||
|
||||
pub enum ImportKind {
|
||||
Function(ImportFunction),
|
||||
Static(ImportStatic),
|
||||
Type(ImportType),
|
||||
}
|
||||
|
||||
pub struct ImportFunction {
|
||||
pub function: Function,
|
||||
pub rust_name: syn::Ident,
|
||||
pub kind: ImportFunctionKind,
|
||||
pub shim: syn::Ident,
|
||||
}
|
||||
|
||||
pub enum ImportFunctionKind {
|
||||
Method { class: String, ty: syn::Type },
|
||||
JsConstructor { class: String, ty: syn::Type },
|
||||
Normal,
|
||||
}
|
||||
|
||||
pub struct ImportStatic {
|
||||
pub vis: syn::Visibility,
|
||||
pub ty: syn::Type,
|
||||
pub shim: syn::Ident,
|
||||
pub rust_name: syn::Ident,
|
||||
pub js_name: syn::Ident,
|
||||
}
|
||||
|
||||
pub struct ImportType {
|
||||
pub vis: syn::Visibility,
|
||||
pub name: syn::Ident,
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: syn::Ident,
|
||||
pub arguments: Vec<Type>,
|
||||
pub ret: Option<Type>,
|
||||
pub opts: BindgenAttrs,
|
||||
pub rust_attrs: Vec<syn::Attribute>,
|
||||
pub rust_decl: Box<syn::FnDecl>,
|
||||
pub rust_vis: syn::Visibility,
|
||||
}
|
||||
|
||||
pub struct Struct {
|
||||
pub name: syn::Ident,
|
||||
}
|
||||
|
||||
pub struct Enum {
|
||||
pub name: syn::Ident,
|
||||
pub variants: Vec<Variant>,
|
||||
}
|
||||
|
||||
pub struct Variant {
|
||||
pub name: syn::Ident,
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
pub enum Type {
|
||||
// special
|
||||
Vector(VectorType, bool),
|
||||
|
||||
ByRef(syn::Type),
|
||||
ByMutRef(syn::Type),
|
||||
ByValue(syn::Type),
|
||||
}
|
||||
|
||||
pub enum VectorType {
|
||||
String,
|
||||
I8,
|
||||
U8,
|
||||
I16,
|
||||
U16,
|
||||
I32,
|
||||
U32,
|
||||
F32,
|
||||
F64,
|
||||
JsValue,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn push_item(&mut self, item: syn::Item, opts: Option<BindgenAttrs>, tokens: &mut Tokens) {
|
||||
match item {
|
||||
syn::Item::Fn(mut f) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||
|
||||
let no_mangle = f.attrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, m)| m.interpret_meta().map(|m| (i, m)))
|
||||
.find(|&(_, ref m)| m.name() == "no_mangle");
|
||||
match no_mangle {
|
||||
Some((i, _)) => {
|
||||
f.attrs.remove(i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
f.to_tokens(tokens);
|
||||
self.exports.push(Export {
|
||||
class: None,
|
||||
method: false,
|
||||
mutable: false,
|
||||
function: Function::from(f, opts),
|
||||
});
|
||||
}
|
||||
syn::Item::Struct(mut s) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut s.attrs));
|
||||
s.to_tokens(tokens);
|
||||
self.structs.push(Struct::from(s, opts));
|
||||
}
|
||||
syn::Item::Impl(mut i) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut i.attrs));
|
||||
i.to_tokens(tokens);
|
||||
self.push_impl(i, opts);
|
||||
}
|
||||
syn::Item::ForeignMod(mut f) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||
self.push_foreign_mod(f, opts);
|
||||
}
|
||||
syn::Item::Enum(mut e) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut e.attrs));
|
||||
e.to_tokens(tokens);
|
||||
self.push_enum(e, opts);
|
||||
}
|
||||
_ => panic!(
|
||||
"#[wasm_bindgen] can only be applied to a function, \
|
||||
struct, enum, impl, or extern block"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_impl(&mut self, item: syn::ItemImpl, _opts: BindgenAttrs) {
|
||||
if item.defaultness.is_some() {
|
||||
panic!("default impls are not supported");
|
||||
}
|
||||
if item.unsafety.is_some() {
|
||||
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 *item.self_ty {
|
||||
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"),
|
||||
};
|
||||
for item in item.items.into_iter() {
|
||||
self.push_impl_item(name, item);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_impl_item(&mut self, class: syn::Ident, item: syn::ImplItem) {
|
||||
let mut 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(m) => m,
|
||||
syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"),
|
||||
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
||||
};
|
||||
match method.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => return,
|
||||
}
|
||||
if method.defaultness.is_some() {
|
||||
panic!("default methods are not supported");
|
||||
}
|
||||
if method.sig.constness.is_some() {
|
||||
panic!("can only bindgen non-const functions");
|
||||
}
|
||||
if method.sig.unsafety.is_some() {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
|
||||
let opts = BindgenAttrs::find(&mut method.attrs);
|
||||
|
||||
let (function, mutable) = Function::from_decl(
|
||||
method.sig.ident,
|
||||
Box::new(method.sig.decl),
|
||||
method.attrs,
|
||||
opts,
|
||||
method.vis,
|
||||
true,
|
||||
);
|
||||
self.exports.push(Export {
|
||||
class: Some(class),
|
||||
method: mutable.is_some(),
|
||||
mutable: mutable.unwrap_or(false),
|
||||
function,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_enum(&mut self, item: syn::ItemEnum, _opts: BindgenAttrs) {
|
||||
match item.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => panic!("only public enums are allowed"),
|
||||
}
|
||||
|
||||
let variants = item.variants
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| {
|
||||
match v.fields {
|
||||
syn::Fields::Unit => (),
|
||||
_ => panic!("Only C-Style enums allowed"),
|
||||
}
|
||||
let value = match v.discriminant {
|
||||
Some((
|
||||
_,
|
||||
syn::Expr::Lit(syn::ExprLit {
|
||||
attrs: _,
|
||||
lit: syn::Lit::Int(ref int_lit),
|
||||
}),
|
||||
)) => {
|
||||
if int_lit.value() > <u32>::max_value() as u64 {
|
||||
panic!("Enums can only support numbers that can be represented as u32");
|
||||
}
|
||||
int_lit.value() as u32
|
||||
}
|
||||
None => i as u32,
|
||||
_ => panic!("Enums may only have number literal values"),
|
||||
};
|
||||
|
||||
Variant {
|
||||
name: v.ident,
|
||||
value,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.enums.push(Enum {
|
||||
name: item.ident,
|
||||
variants,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_foreign_mod(&mut self, f: syn::ItemForeignMod, opts: BindgenAttrs) {
|
||||
match f.abi.name {
|
||||
Some(ref l) if l.value() == "C" => {}
|
||||
None => {}
|
||||
_ => panic!("only foreign mods with the `C` ABI are allowed"),
|
||||
}
|
||||
for mut item in f.items.into_iter() {
|
||||
let item_opts = {
|
||||
let attrs = match item {
|
||||
syn::ForeignItem::Fn(ref mut f) => &mut f.attrs,
|
||||
syn::ForeignItem::Type(ref mut t) => &mut t.attrs,
|
||||
syn::ForeignItem::Static(ref mut s) => &mut s.attrs,
|
||||
_ => panic!("only foreign functions/types allowed for now"),
|
||||
};
|
||||
BindgenAttrs::find(attrs)
|
||||
};
|
||||
let module = item_opts.module().or(opts.module()).map(|s| s.to_string());
|
||||
let js_namespace = item_opts.js_namespace().or(opts.js_namespace());
|
||||
let mut kind = match item {
|
||||
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, item_opts),
|
||||
syn::ForeignItem::Type(t) => self.push_foreign_ty(t),
|
||||
syn::ForeignItem::Static(s) => self.push_foreign_static(s, item_opts),
|
||||
_ => panic!("only foreign functions/types allowed for now"),
|
||||
};
|
||||
|
||||
self.imports.push(Import {
|
||||
module,
|
||||
js_namespace,
|
||||
kind,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_foreign_fn(&mut self, f: syn::ForeignItemFn, opts: BindgenAttrs) -> ImportKind {
|
||||
let js_name = opts.js_name().unwrap_or(f.ident);
|
||||
let mut wasm = Function::from_decl(js_name, f.decl, f.attrs, opts, f.vis, false).0;
|
||||
if wasm.opts.catch() {
|
||||
// TODO: this assumes a whole bunch:
|
||||
//
|
||||
// * The outer type is actually a `Result`
|
||||
// * The error type is a `JsValue`
|
||||
// * The actual type is the first type parameter
|
||||
//
|
||||
// should probably fix this one day...
|
||||
wasm.ret = extract_first_ty_param(wasm.ret.as_ref())
|
||||
.expect("can't `catch` without returning a Result");
|
||||
}
|
||||
|
||||
let kind = if wasm.opts.method() {
|
||||
let class = wasm.arguments
|
||||
.get(0)
|
||||
.expect("methods must have at least one argument");
|
||||
let class = match *class {
|
||||
Type::ByRef(ref t) | Type::ByValue(ref t) => t,
|
||||
Type::ByMutRef(_) => panic!("first method argument cannot be mutable ref"),
|
||||
Type::Vector(..) => panic!("method receivers cannot be vectors"),
|
||||
};
|
||||
let class_name = match *class {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => path,
|
||||
_ => panic!("first argument of method must be a path"),
|
||||
};
|
||||
let class_name = extract_path_ident(class_name)
|
||||
.expect("first argument of method must be a bare type");
|
||||
|
||||
ImportFunctionKind::Method {
|
||||
class: class_name.as_ref().to_string(),
|
||||
ty: class.clone(),
|
||||
}
|
||||
} else if wasm.opts.constructor() {
|
||||
let class = match wasm.ret {
|
||||
Some(Type::ByValue(ref t)) => t,
|
||||
_ => panic!("constructor returns must be bare types"),
|
||||
};
|
||||
let class_name = match *class {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => path,
|
||||
_ => panic!("first argument of method must be a path"),
|
||||
};
|
||||
let class_name = extract_path_ident(class_name)
|
||||
.expect("first argument of method must be a bare type");
|
||||
|
||||
ImportFunctionKind::JsConstructor {
|
||||
class: class_name.as_ref().to_string(),
|
||||
ty: class.clone(),
|
||||
}
|
||||
} else {
|
||||
ImportFunctionKind::Normal
|
||||
};
|
||||
|
||||
let shim = {
|
||||
let ns = match kind {
|
||||
ImportFunctionKind::Normal => "n",
|
||||
ImportFunctionKind::Method { ref class, .. } => class,
|
||||
ImportFunctionKind::JsConstructor { ref class, .. } => class,
|
||||
};
|
||||
format!("__wbg_f_{}_{}_{}", js_name, f.ident, ns)
|
||||
};
|
||||
ImportKind::Function(ImportFunction {
|
||||
function: wasm,
|
||||
kind,
|
||||
rust_name: f.ident,
|
||||
shim: shim.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn push_foreign_ty(&mut self, f: syn::ForeignItemType) -> ImportKind {
|
||||
ImportKind::Type(ImportType {
|
||||
vis: f.vis,
|
||||
name: f.ident,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn push_foreign_static(
|
||||
&mut self,
|
||||
f: syn::ForeignItemStatic,
|
||||
opts: BindgenAttrs,
|
||||
) -> ImportKind {
|
||||
if f.mutability.is_some() {
|
||||
panic!("cannot import mutable globals yet")
|
||||
}
|
||||
let js_name = opts.js_name().unwrap_or(f.ident);
|
||||
let shim = format!("__wbg_static_accessor_{}_{}", js_name, f.ident);
|
||||
ImportKind::Static(ImportStatic {
|
||||
ty: *f.ty,
|
||||
vis: f.vis,
|
||||
rust_name: f.ident,
|
||||
js_name,
|
||||
shim: shim.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn literal(&self, dst: &mut Tokens) -> usize {
|
||||
let mut tmp = Tokens::new();
|
||||
let cnt = {
|
||||
let mut a = literal::LiteralBuilder::new(&mut tmp);
|
||||
Literal::literal(self, &mut a);
|
||||
a.finish()
|
||||
};
|
||||
let cnt = cnt as u32;
|
||||
(quote! {
|
||||
(#cnt >> 0) as u8,
|
||||
(#cnt >> 8) as u8,
|
||||
(#cnt >> 16) as u8,
|
||||
(#cnt >> 24) as u8
|
||||
}).to_tokens(dst);
|
||||
tmp.to_tokens(dst);
|
||||
(cnt as usize) + 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn from(input: syn::ItemFn, opts: BindgenAttrs) -> Function {
|
||||
match input.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => panic!("can only bindgen public functions"),
|
||||
}
|
||||
if input.constness.is_some() {
|
||||
panic!("can only bindgen non-const functions");
|
||||
}
|
||||
if input.unsafety.is_some() {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
|
||||
Function::from_decl(input.ident, input.decl, input.attrs, opts, input.vis, false).0
|
||||
}
|
||||
|
||||
pub fn from_decl(
|
||||
name: syn::Ident,
|
||||
decl: Box<syn::FnDecl>,
|
||||
attrs: Vec<syn::Attribute>,
|
||||
opts: BindgenAttrs,
|
||||
vis: syn::Visibility,
|
||||
allow_self: bool,
|
||||
) -> (Function, Option<bool>) {
|
||||
if decl.variadic.is_some() {
|
||||
panic!("can't bindgen variadic functions")
|
||||
}
|
||||
if decl.generics.params.len() > 0 {
|
||||
panic!("can't bindgen functions with lifetime or type parameters")
|
||||
}
|
||||
|
||||
let mut mutable = None;
|
||||
let arguments = 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) if allow_self => {
|
||||
assert!(mutable.is_none());
|
||||
mutable = Some(a.mutability.is_some());
|
||||
None
|
||||
}
|
||||
_ => panic!("arguments cannot be `self` or ignored"),
|
||||
})
|
||||
.map(|arg| Type::from(&arg.ty))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ret = match decl.output {
|
||||
syn::ReturnType::Default => None,
|
||||
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
||||
};
|
||||
|
||||
(
|
||||
Function {
|
||||
name,
|
||||
arguments,
|
||||
ret,
|
||||
opts,
|
||||
rust_vis: vis,
|
||||
rust_decl: decl,
|
||||
rust_attrs: attrs,
|
||||
},
|
||||
mutable,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_path_ident(path: &syn::Path) -> Option<syn::Ident> {
|
||||
if path.leading_colon.is_some() {
|
||||
return None;
|
||||
}
|
||||
if path.segments.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
match path.segments.first().unwrap().value().arguments {
|
||||
syn::PathArguments::None => {}
|
||||
_ => return None,
|
||||
}
|
||||
path.segments.first().map(|v| v.value().ident)
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn from(ty: &syn::Type) -> Type {
|
||||
match *ty {
|
||||
syn::Type::Reference(ref r) => {
|
||||
match *r.elem {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => {
|
||||
let ident = extract_path_ident(path);
|
||||
match ident.as_ref().map(|s| s.as_ref()) {
|
||||
Some("str") => return Type::Vector(VectorType::String, false),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
syn::Type::Slice(ref slice) => {
|
||||
if let Some(ty) = VectorType::from(&slice.elem) {
|
||||
return Type::Vector(ty, false);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return if r.mutability.is_some() {
|
||||
Type::ByMutRef((*r.elem).clone())
|
||||
} else {
|
||||
Type::ByRef((*r.elem).clone())
|
||||
};
|
||||
}
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) if path.leading_colon.is_none() && path.segments.len() == 1 =>
|
||||
{
|
||||
let seg = path.segments.first().unwrap().into_value();
|
||||
match seg.arguments {
|
||||
syn::PathArguments::None => match seg.ident.as_ref() {
|
||||
"String" => return Type::Vector(VectorType::String, true),
|
||||
_ => {}
|
||||
},
|
||||
syn::PathArguments::AngleBracketed(ref t)
|
||||
if seg.ident == "Vec" && t.args.len() == 1 =>
|
||||
{
|
||||
match **t.args.first().unwrap().value() {
|
||||
syn::GenericArgument::Type(ref t) => {
|
||||
if let Some(ty) = VectorType::from(t) {
|
||||
return Type::Vector(ty, true);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Type::ByValue(ty.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Export {
|
||||
pub fn rust_symbol(&self) -> syn::Ident {
|
||||
let mut generated_name = format!("__wasm_bindgen_generated");
|
||||
if let Some(class) = self.class {
|
||||
generated_name.push_str("_");
|
||||
generated_name.push_str(class.as_ref());
|
||||
}
|
||||
generated_name.push_str("_");
|
||||
generated_name.push_str(self.function.name.as_ref());
|
||||
syn::Ident::from(generated_name)
|
||||
}
|
||||
|
||||
pub fn export_name(&self) -> syn::LitStr {
|
||||
let name = match self.class {
|
||||
Some(class) => {
|
||||
shared::struct_function_export_name(class.as_ref(), self.function.name.as_ref())
|
||||
}
|
||||
None => shared::free_function_export_name(self.function.name.as_ref()),
|
||||
};
|
||||
syn::LitStr::new(&name, Span::def_site())
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportFunction {
|
||||
pub fn infer_getter_property(&self) -> String {
|
||||
self.function.name.as_ref().to_string()
|
||||
}
|
||||
|
||||
pub fn infer_setter_property(&self) -> String {
|
||||
let name = self.function.name.as_ref();
|
||||
assert!(name.starts_with("set_"), "setters must start with `set_`");
|
||||
name[4..].to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct {
|
||||
Struct { name: s.ident }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BindgenAttrs {
|
||||
attrs: Vec<BindgenAttr>,
|
||||
}
|
||||
|
||||
impl BindgenAttrs {
|
||||
pub fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs {
|
||||
let pos = attrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen")
|
||||
.map(|a| a.0);
|
||||
let pos = match pos {
|
||||
Some(i) => i,
|
||||
None => return BindgenAttrs::default(),
|
||||
};
|
||||
syn::parse(attrs.remove(pos).tts.into()).expect("malformed #[wasm_bindgen] attribute")
|
||||
}
|
||||
|
||||
fn module(&self) -> Option<&str> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match *a {
|
||||
BindgenAttr::Module(ref s) => Some(&s[..]),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn catch(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Catch => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn constructor(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Constructor => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn method(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Method => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn js_namespace(&self) -> Option<syn::Ident> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match *a {
|
||||
BindgenAttr::JsNamespace(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn getter(&self) -> Option<Option<syn::Ident>> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match *a {
|
||||
BindgenAttr::Getter(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn setter(&self) -> Option<Option<syn::Ident>> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match *a {
|
||||
BindgenAttr::Setter(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn structural(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Structural => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn js_name(&self) -> Option<syn::Ident> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match *a {
|
||||
BindgenAttr::JsName(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttrs {
|
||||
named!(parse -> Self, alt!(
|
||||
do_parse!(
|
||||
opts: parens!(call!(
|
||||
syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated
|
||||
)) >>
|
||||
(BindgenAttrs {
|
||||
attrs: opts.1.into_iter().collect(),
|
||||
})
|
||||
) => { |s| s }
|
||||
|
|
||||
epsilon!() => { |_| BindgenAttrs { attrs: Vec::new() } }
|
||||
));
|
||||
}
|
||||
|
||||
enum BindgenAttr {
|
||||
Catch,
|
||||
Constructor,
|
||||
Method,
|
||||
JsNamespace(syn::Ident),
|
||||
Module(String),
|
||||
Getter(Option<syn::Ident>),
|
||||
Setter(Option<syn::Ident>),
|
||||
Structural,
|
||||
JsName(syn::Ident),
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttr {
|
||||
named!(parse -> Self, alt!(
|
||||
call!(term, "catch") => { |_| BindgenAttr::Catch }
|
||||
|
|
||||
call!(term, "constructor") => { |_| BindgenAttr::Constructor }
|
||||
|
|
||||
call!(term, "method") => { |_| BindgenAttr::Method }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "getter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: syn!(syn::Ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Getter }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "setter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: syn!(syn::Ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Setter }
|
||||
|
|
||||
call!(term, "structural") => { |_| BindgenAttr::Structural }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_namespace") >>
|
||||
punct!(=) >>
|
||||
ns: syn!(syn::Ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::JsNamespace }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "module") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Module }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_name") >>
|
||||
punct!(=) >>
|
||||
ns: syn!(syn::Ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::JsName }
|
||||
));
|
||||
}
|
||||
|
||||
fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
|
||||
let ty = match ty {
|
||||
Some(t) => t,
|
||||
None => return Some(None),
|
||||
};
|
||||
let ty = match *ty {
|
||||
Type::ByValue(ref t) => t,
|
||||
_ => return None,
|
||||
};
|
||||
let path = match *ty {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => path,
|
||||
_ => return None,
|
||||
};
|
||||
let seg = path.segments.last()?.into_value();
|
||||
let generics = match seg.arguments {
|
||||
syn::PathArguments::AngleBracketed(ref t) => t,
|
||||
_ => return None,
|
||||
};
|
||||
let ty = match *generics.args.first()?.into_value() {
|
||||
syn::GenericArgument::Type(ref t) => t,
|
||||
_ => return None,
|
||||
};
|
||||
match *ty {
|
||||
syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Some(None),
|
||||
_ => {}
|
||||
}
|
||||
Some(Some(Type::from(ty)))
|
||||
}
|
||||
|
||||
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> {
|
||||
if let Some((_span, term, next)) = cursor.term() {
|
||||
if term.as_str() == name {
|
||||
return Ok(((), next));
|
||||
}
|
||||
}
|
||||
syn::parse_error()
|
||||
}
|
||||
|
||||
fn ungroup(input: &syn::Type) -> &syn::Type {
|
||||
match *input {
|
||||
syn::Type::Group(ref t) => &t.elem,
|
||||
_ => input,
|
||||
}
|
||||
}
|
||||
|
||||
impl VectorType {
|
||||
fn from(ty: &syn::Type) -> Option<VectorType> {
|
||||
let path = match *ungroup(ty) {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => path,
|
||||
_ => return None,
|
||||
};
|
||||
match extract_path_ident(path)?.as_ref() {
|
||||
"i8" => Some(VectorType::I8),
|
||||
"u8" => Some(VectorType::U8),
|
||||
"i16" => Some(VectorType::I16),
|
||||
"u16" => Some(VectorType::U16),
|
||||
"i32" => Some(VectorType::I32),
|
||||
"u32" => Some(VectorType::U32),
|
||||
"f32" => Some(VectorType::F32),
|
||||
"f64" => Some(VectorType::F64),
|
||||
"JsValue" => Some(VectorType::JsValue),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn abi_element(&self) -> syn::Ident {
|
||||
match *self {
|
||||
VectorType::String => syn::Ident::from("u8"),
|
||||
VectorType::I8 => syn::Ident::from("i8"),
|
||||
VectorType::U8 => syn::Ident::from("u8"),
|
||||
VectorType::I16 => syn::Ident::from("i16"),
|
||||
VectorType::U16 => syn::Ident::from("u16"),
|
||||
VectorType::I32 => syn::Ident::from("i32"),
|
||||
VectorType::U32 => syn::Ident::from("u32"),
|
||||
VectorType::F32 => syn::Ident::from("f32"),
|
||||
VectorType::F64 => syn::Ident::from("f64"),
|
||||
VectorType::JsValue => syn::Ident::from("JsValue"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for VectorType {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
let me = match *self {
|
||||
VectorType::String => my_quote! { String },
|
||||
VectorType::I8 => my_quote! { Vec<i8> },
|
||||
VectorType::U8 => my_quote! { Vec<u8> },
|
||||
VectorType::I16 => my_quote! { Vec<i16> },
|
||||
VectorType::U16 => my_quote! { Vec<u16> },
|
||||
VectorType::I32 => my_quote! { Vec<i32> },
|
||||
VectorType::U32 => my_quote! { Vec<u32> },
|
||||
VectorType::F32 => my_quote! { Vec<f32> },
|
||||
VectorType::F64 => my_quote! { Vec<f64> },
|
||||
VectorType::JsValue => my_quote! { Vec<JsValue> },
|
||||
};
|
||||
me.to_tokens(tokens);
|
||||
}
|
||||
}
|
621
crates/backend/src/codegen.rs
Normal file
621
crates/backend/src/codegen.rs
Normal 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
25
crates/backend/src/lib.rs
Executable 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);
|
||||
}
|
||||
}
|
319
crates/backend/src/literal.rs
Normal file
319
crates/backend/src/literal.rs
Normal file
@ -0,0 +1,319 @@
|
||||
use ast;
|
||||
use proc_macro2::Span;
|
||||
use quote::{ToTokens, Tokens};
|
||||
use shared;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
pub struct LiteralBuilder<'a> {
|
||||
dst: &'a mut Tokens,
|
||||
cnt: usize,
|
||||
}
|
||||
|
||||
impl<'a> LiteralBuilder<'a> {
|
||||
pub fn new(dst: &'a mut Tokens) -> LiteralBuilder<'a> {
|
||||
LiteralBuilder { dst, cnt: 0 }
|
||||
}
|
||||
|
||||
pub fn finish(self) -> usize {
|
||||
self.cnt
|
||||
}
|
||||
|
||||
fn byte(&mut self, b: u8) {
|
||||
::syn::token::Comma::default().to_tokens(self.dst);
|
||||
self.cnt += 1;
|
||||
b.to_tokens(self.dst);
|
||||
}
|
||||
|
||||
fn char_lit(&mut self, c: char) {
|
||||
let c = c as u32;
|
||||
self.byte(c as u8);
|
||||
self.byte((c >> 8) as u8);
|
||||
self.byte((c >> 16) as u8);
|
||||
self.byte((c >> 24) as u8);
|
||||
}
|
||||
|
||||
fn append(&mut self, s: &str) {
|
||||
for c in s.chars() {
|
||||
self.char_lit(c);
|
||||
}
|
||||
}
|
||||
|
||||
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("\"");
|
||||
self.char_lit(s);
|
||||
self.append("\"");
|
||||
}
|
||||
|
||||
fn as_char(&mut self, tokens: Tokens) {
|
||||
self.append("\"");
|
||||
(quote! {
|
||||
, (((#tokens) as u32) >> 0) as u8
|
||||
, (((#tokens) as u32) >> 8) as u8
|
||||
, (((#tokens) as u32) >> 16) as u8
|
||||
, (((#tokens) as u32) >> 24) as u8
|
||||
}).to_tokens(self.dst);
|
||||
self.cnt += 4;
|
||||
self.append("\"");
|
||||
}
|
||||
|
||||
pub 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("}");
|
||||
}
|
||||
|
||||
pub fn list_of<'b, T, U>(&mut self, list: T)
|
||||
where
|
||||
T: IntoIterator<Item = &'b U>,
|
||||
U: 'b + Literal,
|
||||
{
|
||||
self.list(list, U::literal)
|
||||
}
|
||||
|
||||
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("]");
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Literal {
|
||||
fn literal(&self, a: &mut LiteralBuilder);
|
||||
}
|
||||
|
||||
impl Literal for ast::Program {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("exports", &|a| a.list_of(&self.exports)),
|
||||
("imports", &|a| a.list_of(&self.imports)),
|
||||
("enums", &|a| a.list_of(&self.enums)),
|
||||
("custom_type_names", &|a| {
|
||||
let names = self.exports
|
||||
.iter()
|
||||
.filter_map(|e| e.class)
|
||||
.chain(self.structs.iter().map(|s| s.name))
|
||||
.collect::<BTreeSet<_>>();
|
||||
a.list(&names, |s, a| {
|
||||
let val = shared::name_to_descriptor(s.as_ref());
|
||||
a.fields(&[
|
||||
("descriptor", &|a| a.char(val)),
|
||||
("name", &|a| a.str(s.as_ref())),
|
||||
]);
|
||||
})
|
||||
}),
|
||||
("version", &|a| a.str(&shared::version())),
|
||||
("schema_version", &|a| a.str(&shared::SCHEMA_VERSION)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::Function {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("arguments", &|a| a.list_of(&self.arguments)),
|
||||
("ret", &|a| match self.ret {
|
||||
Some(ref s) => s.literal(a),
|
||||
None => a.append("null"),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::Type {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
match *self {
|
||||
ast::Type::Vector(ast::VectorType::String, true) => a.char(shared::TYPE_STRING),
|
||||
ast::Type::Vector(ast::VectorType::String, false) => a.char(shared::TYPE_BORROWED_STR),
|
||||
ast::Type::Vector(ast::VectorType::U8, true) => a.char(shared::TYPE_VECTOR_U8),
|
||||
ast::Type::Vector(ast::VectorType::U8, false) => a.char(shared::TYPE_SLICE_U8),
|
||||
ast::Type::Vector(ast::VectorType::I8, true) => a.char(shared::TYPE_VECTOR_I8),
|
||||
ast::Type::Vector(ast::VectorType::I8, false) => a.char(shared::TYPE_SLICE_I8),
|
||||
ast::Type::Vector(ast::VectorType::U16, true) => a.char(shared::TYPE_VECTOR_U16),
|
||||
ast::Type::Vector(ast::VectorType::U16, false) => a.char(shared::TYPE_SLICE_U16),
|
||||
ast::Type::Vector(ast::VectorType::I16, true) => a.char(shared::TYPE_VECTOR_I16),
|
||||
ast::Type::Vector(ast::VectorType::I16, false) => a.char(shared::TYPE_SLICE_I16),
|
||||
ast::Type::Vector(ast::VectorType::U32, true) => a.char(shared::TYPE_VECTOR_U32),
|
||||
ast::Type::Vector(ast::VectorType::U32, false) => a.char(shared::TYPE_SLICE_U32),
|
||||
ast::Type::Vector(ast::VectorType::I32, true) => a.char(shared::TYPE_VECTOR_I32),
|
||||
ast::Type::Vector(ast::VectorType::I32, false) => a.char(shared::TYPE_SLICE_I32),
|
||||
ast::Type::Vector(ast::VectorType::F32, true) => a.char(shared::TYPE_VECTOR_F32),
|
||||
ast::Type::Vector(ast::VectorType::F32, false) => a.char(shared::TYPE_SLICE_F32),
|
||||
ast::Type::Vector(ast::VectorType::F64, true) => a.char(shared::TYPE_VECTOR_F64),
|
||||
ast::Type::Vector(ast::VectorType::F64, false) => a.char(shared::TYPE_SLICE_F64),
|
||||
ast::Type::Vector(ast::VectorType::JsValue, true) => {
|
||||
a.char(shared::TYPE_VECTOR_JSVALUE)
|
||||
}
|
||||
ast::Type::Vector(ast::VectorType::JsValue, false) => {
|
||||
panic!("Slices of JsValues not supported")
|
||||
}
|
||||
ast::Type::ByValue(ref t) => {
|
||||
a.as_char(my_quote! {
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR
|
||||
});
|
||||
}
|
||||
ast::Type::ByRef(ref ty) | ast::Type::ByMutRef(ref ty) => {
|
||||
a.as_char(my_quote! {
|
||||
(<#ty as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR |
|
||||
::wasm_bindgen::convert::DESCRIPTOR_CUSTOM_REF_FLAG)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::Export {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("class", &|a| match self.class {
|
||||
Some(ref s) => a.str(s.as_ref()),
|
||||
None => a.append("null"),
|
||||
}),
|
||||
("method", &|a| a.bool(self.method)),
|
||||
("function", &|a| self.function.literal(a)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::Import {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("module", &|a| match self.module {
|
||||
Some(ref s) => a.str(s),
|
||||
None => a.append("null"),
|
||||
}),
|
||||
("js_namespace", &|a| match self.js_namespace {
|
||||
Some(ref s) => a.str(s.as_ref()),
|
||||
None => a.append("null"),
|
||||
}),
|
||||
("kind", &|a| self.kind.literal(a)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::ImportKind {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
match *self {
|
||||
ast::ImportKind::Function(ref f) => f.literal(a),
|
||||
ast::ImportKind::Static(ref s) => s.literal(a),
|
||||
ast::ImportKind::Type(ref t) => t.literal(a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::ImportFunction {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
let mut method = false;
|
||||
let mut js_new = false;
|
||||
let mut class_name = None;
|
||||
match self.kind {
|
||||
ast::ImportFunctionKind::Method { ref class, .. } => {
|
||||
method = true;
|
||||
class_name = Some(class);
|
||||
}
|
||||
ast::ImportFunctionKind::JsConstructor { ref class, .. } => {
|
||||
js_new = true;
|
||||
class_name = Some(class);
|
||||
}
|
||||
ast::ImportFunctionKind::Normal => {}
|
||||
}
|
||||
|
||||
let mut getter = None;
|
||||
let mut setter = None;
|
||||
let structural = self.function.opts.structural();
|
||||
|
||||
if let Some(s) = self.function.opts.getter() {
|
||||
let s = s.map(|s| s.to_string());
|
||||
getter = Some(s.unwrap_or_else(|| self.infer_getter_property()));
|
||||
}
|
||||
if let Some(s) = self.function.opts.setter() {
|
||||
let s = s.map(|s| s.to_string());
|
||||
setter = Some(s.unwrap_or_else(|| self.infer_setter_property()));
|
||||
}
|
||||
a.fields(&[
|
||||
("kind", &|a| a.str("function")),
|
||||
("catch", &|a| a.bool(self.function.opts.catch())),
|
||||
("method", &|a| a.bool(method)),
|
||||
("js_new", &|a| a.bool(js_new)),
|
||||
("structural", &|a| a.bool(structural)),
|
||||
("shim", &|a| a.str(self.shim.as_ref())),
|
||||
("getter", &|a| match getter {
|
||||
Some(ref s) => a.str(s),
|
||||
None => a.append("null"),
|
||||
}),
|
||||
("setter", &|a| match setter {
|
||||
Some(ref s) => a.str(s),
|
||||
None => a.append("null"),
|
||||
}),
|
||||
("function", &|a| self.function.literal(a)),
|
||||
("class", &|a| match class_name {
|
||||
Some(s) => a.str(s),
|
||||
None => a.append("null"),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::Enum {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("variants", &|a| a.list_of(&self.variants)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::Variant {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("value", &|a| a.append(&format!("{}", self.value))),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::ImportStatic {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("kind", &|a| a.str("static")),
|
||||
("name", &|a| a.str(self.js_name.as_ref())),
|
||||
("shim", &|a| a.str(self.shim.as_ref())),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal for ast::ImportType {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[("kind", &|a| a.str("type"))])
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user