wasmer_cli/c_gen/
mod.rs

1//! A convenient little abstraction for building up C expressions and generating
2//! simple C code.
3
4#![allow(dead_code)]
5
6pub mod staticlib_header;
7
8/// An identifier in C.
9pub type CIdent = String;
10
11/// A Type in the C language.
12#[derive(Debug, Clone, Default)]
13#[allow(dead_code)]
14pub enum CType {
15    /// C `void` type.
16    #[default]
17    Void,
18    /// A pointer to some other type.
19    PointerTo {
20        /// Whether the pointer is `const`.
21        is_const: bool,
22        /// The type that the pointer points to.
23        inner: Box<CType>,
24    },
25    /// C 8 bit unsigned integer type.
26    U8,
27    /// C 16 bit unsigned integer type.
28    U16,
29    /// C 32 bit unsigned integer type.
30    U32,
31    /// C 64 bit unsigned integer type.
32    U64,
33    /// C pointer sized unsigned integer type.
34    USize,
35    /// C 8 bit signed integer type.
36    I8,
37    /// C 16 bit signed integer type.
38    I16,
39    /// C 32 bit signed integer type.
40    I32,
41    /// C 64 bit signed integer type.
42    I64,
43    /// C pointer sized signed integer type.
44    ISize,
45    /// A function or function pointer.
46    Function {
47        /// The arguments the function takes.
48        arguments: Vec<CType>,
49        /// The return value if it has one
50        ///
51        /// None is equivalent to Some(Box(Ctype::Void)).
52        return_value: Option<Box<CType>>,
53    },
54    /// C constant array.
55    Array {
56        /// The type of the array.
57        inner: Box<CType>,
58    },
59    /// A user defined type.
60    TypeDef(String),
61}
62
63impl CType {
64    /// Convenience function to get a mutable void pointer type.
65    pub fn void_ptr() -> Self {
66        CType::PointerTo {
67            is_const: false,
68            inner: Box::new(CType::Void),
69        }
70    }
71
72    /// Convenience function to get a const void pointer type.
73    #[allow(dead_code)]
74    pub fn const_void_ptr() -> Self {
75        CType::PointerTo {
76            is_const: true,
77            inner: Box::new(CType::Void),
78        }
79    }
80
81    /// Generate the C source code for a type into the given `String`.
82    fn generate_c(&self, w: &mut String) {
83        match &self {
84            Self::Void => {
85                w.push_str("void");
86            }
87            Self::PointerTo { is_const, inner } => {
88                if *is_const {
89                    w.push_str("const ");
90                }
91                inner.generate_c(w);
92                w.push('*');
93            }
94            Self::U8 => {
95                w.push_str("unsigned char");
96            }
97            Self::U16 => {
98                w.push_str("unsigned short");
99            }
100            Self::U32 => {
101                w.push_str("unsigned int");
102            }
103            Self::U64 => {
104                w.push_str("unsigned long long");
105            }
106            Self::USize => {
107                w.push_str("unsigned size_t");
108            }
109            Self::I8 => {
110                w.push_str("char");
111            }
112            Self::I16 => {
113                w.push_str("short");
114            }
115            Self::I32 => {
116                w.push_str("int");
117            }
118            Self::I64 => {
119                w.push_str("long long");
120            }
121            Self::ISize => {
122                w.push_str("size_t");
123            }
124            Self::Function {
125                arguments,
126                return_value,
127            } => {
128                // function with no, name, assume it's a function pointer
129                #[allow(clippy::borrowed_box)]
130                let ret: CType = return_value
131                    .as_ref()
132                    .map(|i: &Box<CType>| (**i).clone())
133                    .unwrap_or_default();
134                ret.generate_c(w);
135                w.push(' ');
136                w.push_str("(*)");
137                w.push('(');
138                match arguments.len() {
139                    l if l > 1 => {
140                        for arg in &arguments[..arguments.len() - 1] {
141                            arg.generate_c(w);
142                            w.push_str(", ");
143                        }
144                        arguments.last().unwrap().generate_c(w);
145                    }
146                    1 => {
147                        arguments[0].generate_c(w);
148                    }
149                    _ => {}
150                }
151                w.push(')');
152            }
153            Self::Array { inner } => {
154                inner.generate_c(w);
155                w.push_str("[]");
156            }
157            Self::TypeDef(inner) => {
158                w.push_str(inner);
159            }
160        }
161    }
162
163    /// Generate the C source code for a type with a nameinto the given `String`.
164    fn generate_c_with_name(&self, name: &str, w: &mut String) {
165        match &self {
166            Self::PointerTo { .. }
167            | Self::TypeDef { .. }
168            | Self::Void
169            | Self::U8
170            | Self::U16
171            | Self::U32
172            | Self::U64
173            | Self::USize
174            | Self::I8
175            | Self::I16
176            | Self::I32
177            | Self::I64
178            | Self::ISize => {
179                self.generate_c(w);
180                w.push(' ');
181                w.push_str(name);
182            }
183            Self::Function {
184                arguments,
185                return_value,
186            } => {
187                #[allow(clippy::borrowed_box)]
188                let ret: CType = return_value
189                    .as_ref()
190                    .map(|i: &Box<CType>| (**i).clone())
191                    .unwrap_or_default();
192                ret.generate_c(w);
193                w.push(' ');
194                w.push_str(name);
195                w.push('(');
196                match arguments.len() {
197                    l if l > 1 => {
198                        for arg in &arguments[..arguments.len() - 1] {
199                            arg.generate_c(w);
200                            w.push_str(", ");
201                        }
202                        arguments.last().unwrap().generate_c(w);
203                    }
204                    1 => {
205                        arguments[0].generate_c(w);
206                    }
207                    _ => {}
208                }
209                w.push(')');
210            }
211            Self::Array { inner } => {
212                inner.generate_c(w);
213                w.push(' ');
214                w.push_str(name);
215                w.push_str("[]");
216            }
217        }
218    }
219}
220
221/// A statement in the C programming language. This may not be exact to what an
222/// AST would look like or what the C standard says about the C language, it's
223/// simply a structed way to organize data for generating C code.
224#[derive(Debug, Clone)]
225pub enum CStatement {
226    /// A declaration of some kind.
227    Declaration {
228        /// The name of the thing being declared.
229        name: CIdent,
230        /// Whether the thing being declared is `extern`.
231        is_extern: bool,
232        /// Whether the thing being declared is `const`.
233        is_const: bool,
234        /// The type of the thing being declared.
235        ctype: CType,
236        /// The definition of the thing being declared.
237        ///
238        /// This is useful for initializing constant arrays, for example.
239        definition: Option<Box<CStatement>>,
240    },
241
242    /// A literal array of CStatements.
243    LiteralArray {
244        /// The contents of the array.
245        items: Vec<CStatement>,
246    },
247
248    /// A literal constant value, passed through directly as a string.
249    LiteralConstant {
250        /// The raw value acting as a constant.
251        value: String,
252    },
253
254    /// A C-style cast
255    Cast {
256        /// The type to cast to.
257        target_type: CType,
258        /// The thing being cast.
259        expression: Box<CStatement>,
260    },
261
262    /// Typedef one type to another.
263    TypeDef {
264        /// The type of the thing being typedef'd.
265        source_type: CType,
266        /// The new name by which this type may be called.
267        new_name: CIdent,
268    },
269}
270
271impl CStatement {
272    /// Generate C source code for the given CStatement.
273    fn generate_c(&self, w: &mut String) {
274        match &self {
275            Self::Declaration {
276                name,
277                is_extern,
278                is_const,
279                ctype,
280                definition,
281            } => {
282                if *is_const {
283                    w.push_str("const ");
284                }
285                if *is_extern {
286                    w.push_str("extern ");
287                }
288                ctype.generate_c_with_name(name, w);
289                if let Some(def) = definition {
290                    w.push_str(" = ");
291                    def.generate_c(w);
292                }
293                w.push(';');
294                w.push('\n');
295            }
296            Self::LiteralArray { items } => {
297                w.push('{');
298                if !items.is_empty() {
299                    w.push('\n');
300                }
301                for item in items {
302                    w.push('\t');
303                    item.generate_c(w);
304                    w.push(',');
305                    w.push('\n');
306                }
307                w.push('}');
308            }
309            Self::LiteralConstant { value } => {
310                w.push_str(value);
311            }
312            Self::Cast {
313                target_type,
314                expression,
315            } => {
316                w.push('(');
317                target_type.generate_c(w);
318                w.push(')');
319                w.push(' ');
320                expression.generate_c(w);
321            }
322            Self::TypeDef {
323                source_type,
324                new_name,
325            } => {
326                w.push_str("typedef ");
327                // leaky abstraction / hack, doesn't fully solve the problem
328                if let CType::Function { .. } = source_type {
329                    source_type.generate_c_with_name(&format!("(*{new_name})"), w);
330                } else {
331                    source_type.generate_c(w);
332                    w.push(' ');
333                    w.push_str(new_name);
334                }
335                w.push(';');
336                w.push('\n');
337            }
338        }
339    }
340}
341
342/// Generate C source code from some `CStatements` into a String.
343// TODO: add config section
344pub fn generate_c(statements: &[CStatement]) -> String {
345    let mut out = String::new();
346    for statement in statements {
347        statement.generate_c(&mut out);
348    }
349    out
350}
351
352#[cfg(test)]
353mod test {
354    use super::*;
355
356    #[test]
357    fn generate_types() {
358        macro_rules! assert_c_type {
359            ($ctype:expr, $expected:expr) => {
360                let mut w = String::new();
361                let ctype = $ctype;
362                ctype.generate_c(&mut w);
363                assert_eq!(w, $expected);
364            };
365        }
366
367        assert_c_type!(CType::Void, "void");
368        assert_c_type!(CType::void_ptr(), "void*");
369        assert_c_type!(CType::const_void_ptr(), "const void*");
370        assert_c_type!(CType::U8, "unsigned char");
371        assert_c_type!(CType::U16, "unsigned short");
372        assert_c_type!(CType::U32, "unsigned int");
373        assert_c_type!(CType::U64, "unsigned long long");
374        assert_c_type!(CType::USize, "unsigned size_t");
375        assert_c_type!(CType::I8, "char");
376        assert_c_type!(CType::I16, "short");
377        assert_c_type!(CType::I32, "int");
378        assert_c_type!(CType::I64, "long long");
379        assert_c_type!(CType::ISize, "size_t");
380        assert_c_type!(CType::TypeDef("my_type".to_string()), "my_type");
381        assert_c_type!(
382            CType::Function {
383                arguments: vec![CType::U8, CType::ISize],
384                return_value: None
385            },
386            "void (*)(unsigned char, size_t)"
387        );
388        assert_c_type!(
389            CType::Function {
390                arguments: vec![],
391                return_value: Some(Box::new(CType::ISize))
392            },
393            "size_t (*)()"
394        );
395        assert_c_type!(
396            CType::PointerTo {
397                is_const: true,
398                inner: Box::new(CType::PointerTo {
399                    is_const: false,
400                    inner: Box::new(CType::U32)
401                })
402            },
403            "const unsigned int**"
404        );
405        // TODO: test more complicated const correctness rules: there are bugs relating to it.
406    }
407
408    #[test]
409    fn generate_types_with_names() {
410        macro_rules! assert_c_type {
411            ($ctype:expr, $name:literal, $expected:expr) => {
412                let mut w = String::new();
413                let ctype = $ctype;
414                ctype.generate_c_with_name($name, &mut w);
415                assert_eq!(w, $expected);
416            };
417        }
418
419        assert_c_type!(CType::Void, "main", "void main");
420        assert_c_type!(CType::void_ptr(), "data", "void* data");
421        assert_c_type!(CType::const_void_ptr(), "data", "const void* data");
422        assert_c_type!(CType::U8, "data", "unsigned char data");
423        assert_c_type!(CType::U16, "data", "unsigned short data");
424        assert_c_type!(CType::U32, "data", "unsigned int data");
425        assert_c_type!(CType::U64, "data", "unsigned long long data");
426        assert_c_type!(CType::USize, "data", "unsigned size_t data");
427        assert_c_type!(CType::I8, "data", "char data");
428        assert_c_type!(CType::I16, "data", "short data");
429        assert_c_type!(CType::I32, "data", "int data");
430        assert_c_type!(CType::I64, "data", "long long data");
431        assert_c_type!(CType::ISize, "data", "size_t data");
432        assert_c_type!(
433            CType::TypeDef("my_type".to_string()),
434            "data",
435            "my_type data"
436        );
437        assert_c_type!(
438            CType::Function {
439                arguments: vec![CType::U8, CType::ISize],
440                return_value: None
441            },
442            "my_func",
443            "void my_func(unsigned char, size_t)"
444        );
445        assert_c_type!(
446            CType::Function {
447                arguments: vec![],
448                return_value: Some(Box::new(CType::ISize))
449            },
450            "my_func",
451            "size_t my_func()"
452        );
453        assert_c_type!(
454            CType::PointerTo {
455                is_const: true,
456                inner: Box::new(CType::PointerTo {
457                    is_const: false,
458                    inner: Box::new(CType::U32)
459                })
460            },
461            "data",
462            "const unsigned int** data"
463        );
464        // TODO: test more complicated const correctness rules: there are bugs relating to it.
465    }
466
467    #[test]
468    fn generate_expressions_works() {
469        macro_rules! assert_c_expr {
470            ($cexpr:expr, $expected:expr) => {
471                let mut w = String::new();
472                let cexpr = $cexpr;
473                cexpr.generate_c(&mut w);
474                assert_eq!(w, $expected);
475            };
476        }
477
478        assert_c_expr!(
479            CStatement::LiteralConstant {
480                value: "\"Hello, world!\"".to_string()
481            },
482            "\"Hello, world!\""
483        );
484        assert_c_expr!(
485            CStatement::TypeDef {
486                source_type: CType::Function {
487                    arguments: vec![CType::I32, CType::I32],
488                    return_value: None,
489                },
490                new_name: "my_func_ptr".to_string(),
491            },
492            "typedef void (*my_func_ptr)(int, int);\n"
493        );
494        assert_c_expr!(
495            CStatement::LiteralArray {
496                items: vec![
497                    CStatement::LiteralConstant {
498                        value: "1".to_string()
499                    },
500                    CStatement::LiteralConstant {
501                        value: "2".to_string()
502                    },
503                    CStatement::LiteralConstant {
504                        value: "3".to_string()
505                    },
506                ]
507            },
508            "{\n\t1,\n\t2,\n\t3,\n}"
509        );
510        assert_c_expr!(CStatement::LiteralArray { items: vec![] }, "{}");
511        assert_c_expr!(
512            CStatement::Declaration {
513                name: "my_array".to_string(),
514                is_extern: false,
515                is_const: true,
516                ctype: CType::Array {
517                    inner: Box::new(CType::I32)
518                },
519                definition: Some(Box::new(CStatement::LiteralArray {
520                    items: vec![
521                        CStatement::LiteralConstant {
522                            value: "1".to_string()
523                        },
524                        CStatement::LiteralConstant {
525                            value: "2".to_string()
526                        },
527                        CStatement::LiteralConstant {
528                            value: "3".to_string()
529                        },
530                    ]
531                }))
532            },
533            "const int my_array[] = {\n\t1,\n\t2,\n\t3,\n};\n"
534        );
535        assert_c_expr!(
536            CStatement::Declaration {
537                name: "my_array".to_string(),
538                is_extern: true,
539                is_const: true,
540                ctype: CType::Array {
541                    inner: Box::new(CType::I32)
542                },
543                definition: None,
544            },
545            "const extern int my_array[];\n"
546        );
547    }
548}