1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use super::vptr;
use super::{Interface, InterfaceMethod};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::spanned::Spanned;
use syn::Type;

/// Generate an VTable for an interface
pub fn generate(interface: &Interface) -> syn::Result<TokenStream> {
    let interface_ident = &interface.name;
    let vtable_ident = ident(&interface_ident.to_string());
    let parent_field = match interface.parent {
        Some(ref parent) => {
            quote! {
                pub parent: <#parent as com::Interface>::VTable,
            }
        }
        None => quote! {},
    };
    let methods = gen_vtable_methods(interface)?;
    let vis = &interface.visibility;

    Ok(quote!(
        #[allow(non_snake_case, missing_docs)]
        #[repr(C)]
        #vis struct #vtable_ident {
            #parent_field
            #methods
        }
    ))
}

pub fn ident(interface_name: &str) -> Ident {
    format_ident!("{}VTable", interface_name)
}

fn gen_vtable_methods(interface: &Interface) -> syn::Result<TokenStream> {
    let mut methods: Vec<TokenStream> = Vec::new();
    for m in interface.methods.iter() {
        methods.push(gen_vtable_method(&interface.name, m)?);
    }

    Ok(quote!(
        #(#methods)*
    ))
}

fn gen_vtable_method(
    interface_ident: &Ident,
    method: &InterfaceMethod,
) -> syn::Result<TokenStream> {
    let method_ident = format_ident!("{}", crate::utils::snake_to_camel(&method.name.to_string()));
    let vtable_function_signature = gen_vtable_function_signature(interface_ident, method)?;

    Ok(quote!(
        pub #method_ident: #vtable_function_signature,
    ))
}

fn gen_vtable_function_signature(
    interface_ident: &Ident,
    method: &InterfaceMethod,
) -> syn::Result<TokenStream> {
    let params = gen_raw_params(interface_ident, method)?;
    let return_type = &method.ret;

    Ok(quote!(
        unsafe extern "system" fn(#params) #return_type
    ))
}

fn gen_raw_params(interface_ident: &Ident, method: &InterfaceMethod) -> syn::Result<TokenStream> {
    let vptr_ident = vptr::ident(interface_ident);
    let mut params = quote!(
        ::core::ptr::NonNull<#vptr_ident>,
    );

    for param in method.args.iter() {
        params.extend(gen_raw_type(param)?);
    }

    Ok(params)
}

fn gen_raw_type(p: &super::interface::InterfaceMethodArg) -> syn::Result<TokenStream> {
    let t = &*p.ty;
    let ty = match t {
        Type::Path(_) | Type::Ptr(_) if !p.pass_through => {
            return Ok(quote!(<#t as ::com::AbiTransferable>::Abi,))
        }
        Type::Path(_) | Type::Ptr(_) => return Ok(quote!(#t,)),
        Type::Array(_n) => "array type",
        Type::BareFn(_n) => "barefn type",
        Type::Group(_n) => "group type",
        Type::ImplTrait(_n) => "implTrait type",
        Type::Infer(_n) => "infer type",
        Type::Macro(_n) => "typeMacro type",
        Type::Never(_n) => "typeNever type",
        Type::Paren(_n) => "paren type",
        Type::Reference(_n) => "reference type",
        Type::Slice(_n) => "slice type",
        Type::TraitObject(_n) => "traitObject type",
        Type::Tuple(_n) => "tuple type",
        Type::Verbatim(_n) => "verbatim type",
        _ => "other type",
    };
    Err(syn::Error::new(
        t.span(),
        format!("unexpected argument type: {}", ty),
    ))
}