]> git.proxmox.com Git - wasi-libc.git/blob - tools/wasi-headers/src/c_header.rs
43c66f78ef9c93b435977d5c36049a9ed7d52221
[wasi-libc.git] / tools / wasi-headers / src / c_header.rs
1 use heck::ShoutySnakeCase;
2 use witx::*;
3
4 const PROLOGUE: &str = r#"/**
5 * THIS FILE IS AUTO-GENERATED!
6 *
7 * @file
8 * This file describes the WASI interface, consisting of functions, types,
9 * and defined values (macros).
10 *
11 * The interface described here is greatly inspired by [CloudABI]'s clean,
12 * thoughtfully-designed, cabability-oriented, POSIX-style API.
13 *
14 * [CloudABI]: https://github.com/NuxiNL/cloudlibc
15 */
16
17 #ifndef __wasi_api_h
18 #define __wasi_api_h
19
20 #ifndef __wasi__
21 #error <wasi/api.h> is only supported on WASI platforms.
22 #endif
23
24 #include <stddef.h>
25 #include <stdint.h>
26
27 _Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout");
28 _Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout");
29 _Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout");
30 _Static_assert(_Alignof(uint16_t) == 2, "non-wasi data layout");
31 _Static_assert(_Alignof(int32_t) == 4, "non-wasi data layout");
32 _Static_assert(_Alignof(uint32_t) == 4, "non-wasi data layout");
33 _Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout");
34 _Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout");
35
36 #ifdef __cplusplus
37 extern "C" {
38 #endif
39
40 // TODO: Encoding this in witx.
41 #define __WASI_DIRCOOKIE_START (UINT64_C(0))
42 "#;
43
44 const EPILOGUE: &str = r#"#ifdef __cplusplus
45 }
46 #endif
47
48 #endif"#;
49
50 pub fn to_c_header(doc: &Document) -> String {
51 let mut ret = String::new();
52
53 ret.push_str(PROLOGUE);
54
55 for d in doc.datatypes() {
56 print_datatype(&mut ret, &*d);
57 }
58
59 for m in doc.modules() {
60 print_module(&mut ret, doc, &m);
61 }
62
63 ret.push_str(EPILOGUE);
64
65 ret
66 }
67
68 fn print_datatype(ret: &mut String, d: &Datatype) {
69 if !d.docs.is_empty() {
70 ret.push_str("/**\n");
71 for line in d.docs.lines() {
72 ret.push_str(&format!(" * {}\n", line));
73 }
74 ret.push_str(" */\n");
75 }
76
77 match &d.variant {
78 DatatypeVariant::Alias(a) => print_alias(ret, a),
79 DatatypeVariant::Enum(e) => print_enum(ret, e),
80 DatatypeVariant::Flags(f) => print_flags(ret, f),
81 DatatypeVariant::Struct(s) => print_struct(ret, s),
82 DatatypeVariant::Union(u) => print_union(ret, u),
83 DatatypeVariant::Handle(h) => print_handle(ret, h),
84 }
85 }
86
87 fn print_alias(ret: &mut String, a: &AliasDatatype) {
88 match a.to {
89 DatatypeIdent::Array(_) => {
90 // Don't emit arrays as top-level types; instead we special-case
91 // them in places like parameter lists so that we can pass them
92 // as pointer and length pairs.
93 }
94 _ => {
95 if a.name.as_str() == "size" {
96 // Special-case "size" as "__SIZE_TYPE__" -- TODO: Encode this in witx.
97 ret.push_str(&format!(
98 "typedef __SIZE_TYPE__ __wasi_{}_t;\n",
99 ident_name(&a.name)
100 ));
101 } else {
102 ret.push_str(&format!(
103 "typedef {} __wasi_{}_t;\n",
104 datatype_ident_name(&a.to),
105 ident_name(&a.name)
106 ));
107 }
108 ret.push_str("\n");
109 }
110 }
111 }
112
113 fn print_enum(ret: &mut String, e: &EnumDatatype) {
114 ret.push_str(&format!(
115 "typedef {} __wasi_{}_t;\n",
116 intrepr_name(e.repr),
117 ident_name(&e.name)
118 ));
119 ret.push_str("\n");
120
121 for (index, variant) in e.variants.iter().enumerate() {
122 if !variant.docs.is_empty() {
123 ret.push_str("/**\n");
124 for line in variant.docs.lines() {
125 ret.push_str(&format!(" * {}\n", line));
126 }
127 ret.push_str(" */\n");
128 }
129 ret.push_str(&format!(
130 "#define __WASI_{}_{} ((__wasi_{}_t){})\n",
131 ident_name(&e.name).to_shouty_snake_case(),
132 ident_name(&variant.name).to_shouty_snake_case(),
133 ident_name(&e.name),
134 index
135 ));
136 ret.push_str("\n");
137 }
138 }
139
140 fn print_flags(ret: &mut String, f: &FlagsDatatype) {
141 ret.push_str(&format!(
142 "typedef {} __wasi_{}_t;\n",
143 intrepr_name(f.repr),
144 ident_name(&f.name)
145 ));
146 ret.push_str("\n");
147
148 for (index, flag) in f.flags.iter().enumerate() {
149 if !flag.docs.is_empty() {
150 ret.push_str("/**\n");
151 for line in flag.docs.lines() {
152 ret.push_str(&format!(" * {}\n", line));
153 }
154 ret.push_str(" */\n");
155 }
156 ret.push_str(&format!(
157 "#define __WASI_{}_{} ((__wasi_{}_t){})\n",
158 ident_name(&f.name).to_shouty_snake_case(),
159 ident_name(&flag.name).to_shouty_snake_case(),
160 ident_name(&f.name),
161 1u128 << index
162 ));
163 ret.push_str("\n");
164 }
165 }
166
167 fn print_struct(ret: &mut String, s: &StructDatatype) {
168 ret.push_str(&format!(
169 "typedef struct __wasi_{}_t {{\n",
170 ident_name(&s.name)
171 ));
172
173 for member in &s.members {
174 if !member.docs.is_empty() {
175 ret.push_str(" /**\n");
176 for line in member.docs.lines() {
177 ret.push_str(&format!(" * {}\n", line));
178 }
179 ret.push_str(" */\n");
180 }
181 ret.push_str(&format!(
182 " {} {};\n",
183 datatype_ident_name(&member.type_),
184 ident_name(&member.name)
185 ));
186 ret.push_str("\n");
187 }
188
189 ret.push_str(&format!("}} __wasi_{}_t;\n", ident_name(&s.name)));
190 ret.push_str("\n");
191 }
192
193 fn print_union(ret: &mut String, u: &UnionDatatype) {
194 ret.push_str(&format!(
195 "typedef union __wasi_{}_t {{\n",
196 ident_name(&u.name)
197 ));
198
199 for variant in &u.variants {
200 if !variant.docs.is_empty() {
201 ret.push_str(" /**\n");
202 for line in variant.docs.lines() {
203 ret.push_str(&format!(" * {}\n", line));
204 }
205 ret.push_str(" */\n");
206 }
207 ret.push_str(&format!(
208 " {} {};\n",
209 datatype_ident_name(&variant.type_),
210 ident_name(&variant.name)
211 ));
212 ret.push_str("\n");
213 }
214
215 ret.push_str(&format!("}} __wasi_{}_t;\n", ident_name(&u.name)));
216 ret.push_str("\n");
217 }
218
219 fn print_handle(ret: &mut String, h: &HandleDatatype) {
220 ret.push_str(&format!("typedef int __wasi_{}_t;", ident_name(&h.name)));
221 }
222
223 fn print_module(ret: &mut String, doc: &Document, m: &Module) {
224 ret.push_str("/**\n");
225 ret.push_str(&format!(" * @defgroup {}\n", ident_name(&m.name),));
226 for line in m.docs.lines() {
227 ret.push_str(&format!(" * {}\n", line));
228 }
229 ret.push_str(" * @{\n");
230 ret.push_str(" */\n");
231 ret.push_str("\n");
232
233 for func in m.funcs() {
234 print_func(ret, doc, &func, &m.name);
235 }
236
237 ret.push_str("/** @} */\n");
238 ret.push_str("\n");
239 }
240
241 fn print_func(ret: &mut String, doc: &Document, func: &InterfaceFunc, module_name: &Id) {
242 if !func.docs.is_empty() {
243 ret.push_str("/**\n");
244 for line in func.docs.lines() {
245 ret.push_str(&format!(" * {}\n", line));
246 }
247 if !func.results.is_empty() {
248 let first_result = &func.results[0];
249 if !first_result.docs.is_empty() {
250 ret.push_str(" * @return\n");
251 for line in first_result.docs.lines() {
252 ret.push_str(&format!(" * {}", line));
253 }
254 }
255 }
256 ret.push_str(" */\n");
257 }
258 if func.results.is_empty() {
259 // Special-case "proc_exit" as _Noreturn -- TODO: Encode this in witx.
260 if func.name.as_str() == "proc_exit" {
261 ret.push_str("_Noreturn ");
262 }
263 ret.push_str("void ");
264 } else {
265 let first_result = &func.results[0];
266 ret.push_str(&format!("{} ", datatype_ident_name(&first_result.type_)));
267 }
268
269 ret.push_str(&format!("__wasi_{}(\n", ident_name(&func.name)));
270
271 if func.params.is_empty() && func.results.len() <= 1 {
272 ret.push_str(" void\n");
273 }
274 for (index, param) in func.params.iter().enumerate() {
275 if !param.docs.is_empty() {
276 ret.push_str(" /**\n");
277 for line in param.docs.lines() {
278 ret.push_str(&format!(" * {}\n", line));
279 }
280 ret.push_str(" */\n");
281 }
282 add_params(ret, doc, &ident_name(&param.name), &param.type_);
283 ret.push_str(&format!(
284 "{}\n",
285 if index + 1 < func.params.len() || func.results.len() > 1 {
286 ",\n"
287 } else {
288 ""
289 }
290 ));
291 }
292
293 for (index, result) in func.results.iter().enumerate() {
294 if index == 0 {
295 // The first result is returned by value above.
296 continue;
297 }
298 if !result.docs.is_empty() {
299 ret.push_str(" /**\n");
300 for line in result.docs.lines() {
301 ret.push_str(&format!(" * {}\n", line));
302 }
303 ret.push_str(" */\n");
304 }
305 ret.push_str(&format!(
306 " {} *{}{}\n",
307 datatype_ident_name(&result.type_),
308 ident_name(&result.name),
309 if index + 1 < func.results.len() {
310 ","
311 } else {
312 ""
313 }
314 ));
315 }
316
317 ret.push_str(") __attribute__((\n");
318 ret.push_str(&format!(
319 " __import_module__(\"{}\"),\n",
320 ident_name(module_name)
321 ));
322 ret.push_str(&format!(
323 " __import_name__(\"{}\")",
324 ident_name(&func.name)
325 ));
326 if !func.results.is_empty() {
327 ret.push_str(",\n __warn_unused_result__\n");
328 }
329 ret.push_str("));\n");
330 ret.push_str("\n");
331 }
332
333 fn add_params(ret: &mut String, doc: &Document, name: &str, type_: &DatatypeIdent) {
334 match type_ {
335 DatatypeIdent::Ident(i) => match &doc.datatype(&i.name).unwrap().as_ref().variant {
336 DatatypeVariant::Alias(a) => add_resolved_params(ret, name, &a.to),
337 _ => add_resolved_params(ret, name, type_),
338 },
339 _ => add_resolved_params(ret, name, type_),
340 }
341 }
342
343 fn add_resolved_params(ret: &mut String, name: &str, type_: &DatatypeIdent) {
344 match type_ {
345 DatatypeIdent::Builtin(BuiltinType::String) => {
346 ret.push_str(&format!(" const char *{},\n", name));
347 ret.push_str("\n");
348 ret.push_str(" /**\n");
349 ret.push_str(&format!(
350 " * The length of the buffer pointed to by `{}`.\n",
351 name,
352 ));
353 ret.push_str(" */\n");
354 ret.push_str(&format!(" size_t {}_len", name));
355 }
356 DatatypeIdent::Array(element) => {
357 ret.push_str(&format!(
358 " const {} *{},\n",
359 datatype_ident_name(&element),
360 name
361 ));
362 ret.push_str("\n");
363 ret.push_str(" /**\n");
364 ret.push_str(&format!(
365 " * The length of the array pointed to by `{}`.\n",
366 name,
367 ));
368 ret.push_str(" */\n");
369 ret.push_str(&format!(" size_t {}_len", name));
370 }
371 _ => {
372 ret.push_str(&format!(" {} {}", datatype_ident_name(&type_), name));
373 }
374 }
375 }
376
377 fn ident_name(i: &Id) -> String {
378 i.as_str().to_string()
379 }
380
381 fn builtin_type_name(b: BuiltinType) -> &'static str {
382 match b {
383 BuiltinType::String => "string",
384 BuiltinType::U8 => "uint8_t",
385 BuiltinType::U16 => "uint16_t",
386 BuiltinType::U32 => "uint32_t",
387 BuiltinType::U64 => "uint64_t",
388 BuiltinType::S8 => "int8_t",
389 BuiltinType::S16 => "int16_t",
390 BuiltinType::S32 => "int32_t",
391 BuiltinType::S64 => "int64_t",
392 BuiltinType::F32 => "float",
393 BuiltinType::F64 => "double",
394 }
395 }
396
397 fn datatype_ident_name(data_ty: &DatatypeIdent) -> String {
398 match data_ty {
399 DatatypeIdent::Builtin(b) => builtin_type_name(*b).to_string(),
400 DatatypeIdent::Array(_) => unreachable!("arrays should be special-cased"),
401 DatatypeIdent::Pointer(p) => format!("{} *", datatype_ident_name(&*p)),
402 DatatypeIdent::ConstPointer(p) => format!("const {} *", datatype_ident_name(&*p)),
403 DatatypeIdent::Ident(i) => format!("__wasi_{}_t", i.name.as_str()),
404 }
405 }
406
407 fn intrepr_name(i: IntRepr) -> &'static str {
408 match i {
409 IntRepr::U8 => "uint8_t",
410 IntRepr::U16 => "uint16_t",
411 IntRepr::U32 => "uint32_t",
412 IntRepr::U64 => "uint64_t",
413 }
414 }