ligen_core/generator/
ffi_generator.rs

1//! FFI generator module.
2
3use crate::prelude::*;
4
5use crate::generator::{Context, ImplementationVisitor, FunctionVisitor};
6use crate::ir::{Type, Identifier, Visibility, ImplementationItem};
7use crate::ir::processing::ReplaceIdentifier;
8
9/// FFI generator.
10pub trait FFIGenerator {
11    /// Generate FFI.
12    fn generate_ffi(&self, context: &Context, visitor: Option<&ImplementationVisitor>) -> TokenStream;
13}
14
15/// A generic FFI generator which can be used for most languages.
16pub trait GenericFFIGenerator {
17    /// Generate the function parameters.
18    fn generate_parameters(_context: &Context, function: &FunctionVisitor) -> TokenStream {
19        let object_identifier = function.parent.current.self_.path().last();
20        function
21            .current
22            .inputs
23            .iter()
24            .fold(TokenStream::new(), |mut tokens, parameter| {
25                let type_ = Self::to_marshal_parameter(&parameter.type_);
26                let identifier = self_to_explicit_name(&parameter.identifier, &object_identifier);
27                tokens.append_all(quote! {#identifier: #type_,});
28                tokens
29            })
30    }
31
32    /// Generate the function call arguments and its conversions.
33    fn generate_arguments(_context: &Context, function: &FunctionVisitor) -> TokenStream {
34        let object_identifier = function.parent.current.self_.path().last();
35        function
36            .current
37            .inputs
38            .iter()
39            .fold(TokenStream::new(), |mut tokens, parameter| {
40                let identifier = self_to_explicit_name(&parameter.identifier, &object_identifier);
41                tokens.append_all(quote! {#identifier.into(),});
42                tokens
43            })
44    }
45
46    /// Marshal type.
47    fn to_marshal_output(type_: &Type) -> TokenStream {
48        match type_ {
49            Type::Compound(path) => match path.segments.last().unwrap().name.as_str() {
50                "String" => quote! { *mut crate::ffi::RString },
51                _ => quote! { *mut #type_ },
52            },
53            _ => quote! { #type_ },
54        }
55    }
56
57    /// Marshal type.
58    fn to_marshal_parameter(type_: &Type) -> TokenStream {
59        match type_ {
60            Type::Compound(path) => match path.segments.last().unwrap().name.as_str() {
61                "String" => quote! { crate::ffi::CChar },
62                _ => quote! { *mut #type_ },
63            },
64            _ => quote! { #type_ },
65        }
66    }
67
68    /// Generate the function output.
69    fn generate_output(_context: &Context, output: &Option<Type>) -> TokenStream {
70        match output {
71            Some(type_) => Self::to_marshal_output(type_),
72            _ => quote! {()},
73        }
74    }
75
76    /// Generate the function
77    fn generate_function_signature(
78        context: &Context,
79        visitor: &FunctionVisitor,
80    ) -> TokenStream {
81        let implementation = &visitor.parent.current;
82        let function = &visitor.current;
83        let parameters = Self::generate_parameters(context, visitor);
84        let output = Self::generate_output(context, &function.output);
85        let function_name = format!("{}_{}", implementation.self_.path().last().name, function.identifier.name);
86        let function_identifier = Identifier::new(&function_name);
87        quote! {
88            #[no_mangle]
89            pub extern fn #function_identifier(#parameters) -> #output
90        }
91    }
92
93    /// Generate the function
94    fn generate_function_block(
95        context: &Context,
96        visitor: &FunctionVisitor,
97    ) -> TokenStream {
98        let method = &visitor.current;
99        let implementation = &visitor.parent.current;
100        let arguments = Self::generate_arguments(context, visitor);
101        let self_identifier = &implementation.self_;
102        let method_identifier = &method.identifier;
103        let result = if let Some(Type::Compound(_identifier)) = method.output.as_ref() {
104            quote! {
105                Box::into_raw(Box::new(result.into()))
106            }
107        } else {
108            quote! {result}
109        };
110        quote! {
111            {
112                let result = #self_identifier::#method_identifier(#arguments);
113                #result
114            }
115        }
116    }
117
118    /// Generate an extern function for an implementation method.
119    fn generate_function(
120        context: &Context,
121        visitor: &FunctionVisitor,
122    ) -> TokenStream {
123        if let Visibility::Public = visitor.current.visibility {
124            let function_signature = Self::generate_function_signature(context, visitor);
125            let method_block = Self::generate_function_block(context, visitor);
126            quote! { #function_signature #method_block }
127        } else {
128            quote! {}
129        }
130    }
131
132    /// Generate drop extern.
133    fn generate_drop(visitor: &ImplementationVisitor) -> TokenStream {
134        let self_path = visitor.current.self_.path();
135        let object_name = self_path.last();
136        let drop_name = Identifier::new(format!("{}_drop", object_name.name).as_str());
137        quote! {
138            #[no_mangle]
139            pub unsafe extern fn #drop_name(object: *mut #object_name) {
140                Box::from_raw(object);
141            }
142        }
143    }
144
145    /// Generate externs for Constants and Methods.
146    fn generate(context: &Context, implementation: &ImplementationVisitor) -> TokenStream {
147        let mut tokens =
148            implementation
149                .current
150                .items
151                .iter()
152                .fold(TokenStream::new(), |mut tokens, item| {
153                    match item {
154                        ImplementationItem::Constant(_) => unimplemented!("Constants aren't implemented yet."),
155                        ImplementationItem::Method(method) => tokens.append_all(Self::generate_function(context, &implementation.child(method.clone()))),
156                    }
157                    tokens
158                });
159        tokens.append_all(Self::generate_drop(&implementation));
160        tokens
161    }
162}
163
164fn self_to_explicit_name(identifier: &Identifier, name_identifier: &Identifier) -> Identifier {
165    let mut identifier = identifier.clone();
166    identifier.replace_identifier(&Identifier::new("self"), &Identifier::new(name_identifier.name.to_lowercase()));
167    identifier
168}
169
170impl<T: GenericFFIGenerator> FFIGenerator for T {
171    fn generate_ffi(&self, context: &Context, implementation: Option<&ImplementationVisitor>) -> TokenStream {
172        implementation
173            .map(|implementation| Self::generate(context, implementation))
174            .unwrap_or_else(|| TokenStream::new())
175    }
176}