]> git.proxmox.com Git - wasi-libc.git/blame - tools/wasi-headers/src/c_header.rs
gen-headers: make handle output consistient with prior
[wasi-libc.git] / tools / wasi-headers / src / c_header.rs
CommitLineData
446cb3f1
DG
1use heck::ShoutySnakeCase;
2use witx::*;
3
c6f2c051 4pub fn to_c_header(doc: &Document, inputs_str: &str) -> String {
cd74e1d9
DG
5 let mut ret = String::new();
6
7 ret.push_str(&format!(
8 r#"/**
9 * THIS FILE IS AUTO-GENERATED from the following files:
10 * {}
446cb3f1
DG
11 *
12 * @file
cd74e1d9 13 * This file describes the [WASI] interface, consisting of functions, types,
446cb3f1
DG
14 * and defined values (macros).
15 *
16 * The interface described here is greatly inspired by [CloudABI]'s clean,
37c663f2 17 * thoughtfully-designed, capability-oriented, POSIX-style API.
446cb3f1
DG
18 *
19 * [CloudABI]: https://github.com/NuxiNL/cloudlibc
cd74e1d9 20 * [WASI]: https://github.com/WebAssembly/WASI/
446cb3f1
DG
21 */
22
23#ifndef __wasi_api_h
24#define __wasi_api_h
25
26#ifndef __wasi__
27#error <wasi/api.h> is only supported on WASI platforms.
28#endif
29
c6f2c051
PH
30#ifndef __wasm32__
31#error <wasi/api.h> only supports wasm32; doesn't yet support wasm64
32#endif
33
446cb3f1
DG
34#include <stddef.h>
35#include <stdint.h>
36
37_Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout");
38_Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout");
39_Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout");
40_Static_assert(_Alignof(uint16_t) == 2, "non-wasi data layout");
41_Static_assert(_Alignof(int32_t) == 4, "non-wasi data layout");
42_Static_assert(_Alignof(uint32_t) == 4, "non-wasi data layout");
43_Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout");
44_Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout");
c6f2c051 45_Static_assert(_Alignof(void*) == 4, "non-wasi data layout");
446cb3f1
DG
46
47#ifdef __cplusplus
cd74e1d9 48extern "C" {{
446cb3f1
DG
49#endif
50
51// TODO: Encoding this in witx.
52#define __WASI_DIRCOOKIE_START (UINT64_C(0))
cd74e1d9
DG
53"#,
54 inputs_str,
55 ));
446cb3f1 56
fe130532
PH
57 for nt in doc.typenames() {
58 print_datatype(&mut ret, &*nt);
446cb3f1
DG
59 }
60
61 for m in doc.modules() {
fe130532 62 print_module(&mut ret, &m);
446cb3f1
DG
63 }
64
cd74e1d9
DG
65 ret.push_str(
66 r#"#ifdef __cplusplus
67}
68#endif
69
70#endif
71"#,
72 );
446cb3f1
DG
73
74 ret
75}
76
fe130532
PH
77fn print_datatype(ret: &mut String, nt: &NamedType) {
78 if !nt.docs.is_empty() {
446cb3f1 79 ret.push_str("/**\n");
fe130532 80 for line in nt.docs.lines() {
446cb3f1
DG
81 ret.push_str(&format!(" * {}\n", line));
82 }
83 ret.push_str(" */\n");
84 }
85
ec86d4de 86 match &nt.tref {
fe130532
PH
87 TypeRef::Value(v) => match &**v {
88 Type::Enum(e) => print_enum(ret, &nt.name, e),
ec86d4de 89 Type::Int(i) => print_int(ret, &nt.name, i),
fe130532
PH
90 Type::Flags(f) => print_flags(ret, &nt.name, f),
91 Type::Struct(s) => print_struct(ret, &nt.name, s),
92 Type::Union(u) => print_union(ret, &nt.name, u),
93 Type::Handle(h) => print_handle(ret, &nt.name, h),
94 Type::Builtin { .. }
95 | Type::Array { .. }
96 | Type::Pointer { .. }
ec86d4de 97 | Type::ConstPointer { .. } => print_alias(ret, &nt.name, &nt.tref),
fe130532 98 },
ec86d4de 99 TypeRef::Name(_) => print_alias(ret, &nt.name, &nt.tref),
446cb3f1
DG
100 }
101}
102
fe130532
PH
103fn print_alias(ret: &mut String, name: &Id, dest: &TypeRef) {
104 match &*dest.type_() {
105 Type::Array(_) => {
446cb3f1
DG
106 // Don't emit arrays as top-level types; instead we special-case
107 // them in places like parameter lists so that we can pass them
108 // as pointer and length pairs.
109 }
110 _ => {
fe130532 111 if name.as_str() == "size" {
446cb3f1
DG
112 // Special-case "size" as "__SIZE_TYPE__" -- TODO: Encode this in witx.
113 ret.push_str(&format!(
114 "typedef __SIZE_TYPE__ __wasi_{}_t;\n",
fe130532 115 ident_name(name)
446cb3f1
DG
116 ));
117 } else {
118 ret.push_str(&format!(
119 "typedef {} __wasi_{}_t;\n",
fe130532
PH
120 typeref_name(dest),
121 ident_name(name)
446cb3f1
DG
122 ));
123 }
124 ret.push_str("\n");
c6f2c051
PH
125
126 ret.push_str(&format!(
127 "_Static_assert(sizeof(__wasi_{}_t) == {}, \"witx calculated size\");\n",
128 ident_name(name),
129 dest.mem_size_align().size
130 ));
131 ret.push_str(&format!(
132 "_Static_assert(_Alignof(__wasi_{}_t) == {}, \"witx calculated align\");\n",
133 ident_name(name),
134 dest.mem_size_align().align
135 ));
136
137 ret.push_str("\n");
446cb3f1
DG
138 }
139 }
140}
141
fe130532 142fn print_enum(ret: &mut String, name: &Id, e: &EnumDatatype) {
446cb3f1
DG
143 ret.push_str(&format!(
144 "typedef {} __wasi_{}_t;\n",
145 intrepr_name(e.repr),
fe130532 146 ident_name(name)
446cb3f1
DG
147 ));
148 ret.push_str("\n");
149
150 for (index, variant) in e.variants.iter().enumerate() {
151 if !variant.docs.is_empty() {
152 ret.push_str("/**\n");
153 for line in variant.docs.lines() {
154 ret.push_str(&format!(" * {}\n", line));
155 }
156 ret.push_str(" */\n");
157 }
158 ret.push_str(&format!(
dd010bee 159 "#define __WASI_{}_{} ({}({}))\n",
fe130532 160 ident_name(&name).to_shouty_snake_case(),
446cb3f1 161 ident_name(&variant.name).to_shouty_snake_case(),
dd010bee 162 intrepr_const(e.repr),
446cb3f1
DG
163 index
164 ));
165 ret.push_str("\n");
166 }
c6f2c051
PH
167
168 ret.push_str(&format!(
169 "_Static_assert(sizeof(__wasi_{}_t) == {}, \"witx calculated size\");\n",
170 ident_name(name),
171 e.repr.mem_size()
172 ));
173 ret.push_str(&format!(
174 "_Static_assert(_Alignof(__wasi_{}_t) == {}, \"witx calculated align\");\n",
175 ident_name(name),
176 e.repr.mem_align()
177 ));
178
179 ret.push_str("\n");
446cb3f1
DG
180}
181
ec86d4de
PH
182fn print_int(ret: &mut String, name: &Id, i: &IntDatatype) {
183 ret.push_str(&format!(
184 "typedef {} __wasi_{}_t;\n",
185 intrepr_name(i.repr),
186 ident_name(name)
187 ));
188 ret.push_str("\n");
189
190 for (index, const_) in i.consts.iter().enumerate() {
191 if !const_.docs.is_empty() {
192 ret.push_str("/**\n");
193 for line in const_.docs.lines() {
194 ret.push_str(&format!(" * {}\n", line));
195 }
196 ret.push_str(" */\n");
197 }
198 ret.push_str(&format!(
199 "#define __WASI_{}_{} ({}({}))\n",
200 ident_name(&name).to_shouty_snake_case(),
201 ident_name(&const_.name).to_shouty_snake_case(),
202 intrepr_const(i.repr),
203 index
204 ));
205 ret.push_str("\n");
206 }
207
208 ret.push_str(&format!(
209 "_Static_assert(sizeof(__wasi_{}_t) == {}, \"witx calculated size\");\n",
210 ident_name(name),
211 i.repr.mem_size()
212 ));
213 ret.push_str(&format!(
214 "_Static_assert(_Alignof(__wasi_{}_t) == {}, \"witx calculated align\");\n",
215 ident_name(name),
216 i.repr.mem_align()
217 ));
218
219 ret.push_str("\n");
220}
221
fe130532 222fn print_flags(ret: &mut String, name: &Id, f: &FlagsDatatype) {
446cb3f1
DG
223 ret.push_str(&format!(
224 "typedef {} __wasi_{}_t;\n",
225 intrepr_name(f.repr),
fe130532 226 ident_name(name)
446cb3f1
DG
227 ));
228 ret.push_str("\n");
229
230 for (index, flag) in f.flags.iter().enumerate() {
231 if !flag.docs.is_empty() {
232 ret.push_str("/**\n");
233 for line in flag.docs.lines() {
234 ret.push_str(&format!(" * {}\n", line));
235 }
236 ret.push_str(" */\n");
237 }
238 ret.push_str(&format!(
dd010bee 239 "#define __WASI_{}_{} ({}({}))\n",
fe130532 240 ident_name(name).to_shouty_snake_case(),
446cb3f1 241 ident_name(&flag.name).to_shouty_snake_case(),
dd010bee 242 intrepr_const(f.repr),
446cb3f1
DG
243 1u128 << index
244 ));
245 ret.push_str("\n");
246 }
c6f2c051
PH
247
248 ret.push_str(&format!(
249 "_Static_assert(sizeof(__wasi_{}_t) == {}, \"witx calculated size\");\n",
250 ident_name(name),
251 f.repr.mem_size(),
252 ));
253 ret.push_str(&format!(
254 "_Static_assert(_Alignof(__wasi_{}_t) == {}, \"witx calculated align\");\n",
255 ident_name(name),
256 f.repr.mem_align(),
257 ));
258
259 ret.push_str("\n");
446cb3f1
DG
260}
261
fe130532 262fn print_struct(ret: &mut String, name: &Id, s: &StructDatatype) {
446cb3f1
DG
263 ret.push_str(&format!(
264 "typedef struct __wasi_{}_t {{\n",
fe130532 265 ident_name(name)
446cb3f1
DG
266 ));
267
268 for member in &s.members {
269 if !member.docs.is_empty() {
270 ret.push_str(" /**\n");
271 for line in member.docs.lines() {
272 ret.push_str(&format!(" * {}\n", line));
273 }
274 ret.push_str(" */\n");
275 }
276 ret.push_str(&format!(
277 " {} {};\n",
fe130532 278 typeref_name(&member.tref),
446cb3f1
DG
279 ident_name(&member.name)
280 ));
281 ret.push_str("\n");
282 }
283
fe130532 284 ret.push_str(&format!("}} __wasi_{}_t;\n", ident_name(name)));
446cb3f1 285 ret.push_str("\n");
c6f2c051
PH
286
287 ret.push_str(&format!(
288 "_Static_assert(sizeof(__wasi_{}_t) == {}, \"witx calculated size\");\n",
289 ident_name(name),
290 s.mem_size()
291 ));
292 ret.push_str(&format!(
293 "_Static_assert(_Alignof(__wasi_{}_t) == {}, \"witx calculated align\");\n",
294 ident_name(name),
295 s.mem_align()
296 ));
297
298 for layout in s.member_layout() {
299 ret.push_str(&format!(
300 "_Static_assert(offsetof(__wasi_{}_t, {}) == {}, \"witx calculated offset\");\n",
301 ident_name(name),
302 ident_name(&layout.member.name),
303 layout.offset
304 ));
305 }
306
307 ret.push_str("\n");
446cb3f1
DG
308}
309
fe130532 310fn print_union(ret: &mut String, name: &Id, u: &UnionDatatype) {
71d279a4
PH
311 ret.push_str(&format!(
312 "typedef union __wasi_{}_u_t {{\n",
313 ident_name(name)
314 ));
446cb3f1
DG
315
316 for variant in &u.variants {
71d279a4
PH
317 if let Some(ref tref) = variant.tref {
318 if !variant.docs.is_empty() {
319 ret.push_str(" /**\n");
320 for line in variant.docs.lines() {
321 ret.push_str(&format!(" * {}\n", line));
322 }
323 ret.push_str(" */\n");
446cb3f1 324 }
71d279a4
PH
325 ret.push_str(&format!(
326 " {} {};\n",
327 typeref_name(tref),
328 ident_name(&variant.name)
329 ));
446cb3f1 330 }
446cb3f1 331 }
71d279a4
PH
332 ret.push_str(&format!("}} __wasi_{}_u_t;\n", ident_name(name)));
333
334 ret.push_str(&format!(
335 "typedef struct __wasi_{}_t {{\n",
336 ident_name(name)
337 ));
338
d415ae65 339 ret.push_str(&format!(" {} tag;\n", namedtype_name(&u.tag)));
71d279a4 340 ret.push_str(&format!(" __wasi_{}_u_t u;\n", ident_name(name)));
446cb3f1 341
fe130532 342 ret.push_str(&format!("}} __wasi_{}_t;\n", ident_name(name)));
446cb3f1 343 ret.push_str("\n");
c6f2c051
PH
344
345 ret.push_str(&format!(
346 "_Static_assert(sizeof(__wasi_{}_t) == {}, \"witx calculated size\");\n",
347 ident_name(name),
348 u.mem_size()
349 ));
350 ret.push_str(&format!(
351 "_Static_assert(_Alignof(__wasi_{}_t) == {}, \"witx calculated align\");\n",
352 ident_name(name),
353 u.mem_align()
354 ));
355
71d279a4
PH
356 let l = u.union_layout();
357 ret.push_str(&format!(
358 "_Static_assert(offsetof(__wasi_{}_t, u) == {}, \"witx calculated union offset\");\n",
359 ident_name(name),
360 l.contents_offset,
361 ));
362 ret.push_str(&format!(
363 "_Static_assert(sizeof(__wasi_{}_u_t) == {}, \"witx calculated union size\");\n",
364 ident_name(name),
365 l.contents_size,
366 ));
367 ret.push_str(&format!(
368 "_Static_assert(_Alignof(__wasi_{}_u_t) == {}, \"witx calculated union align\");\n",
369 ident_name(name),
370 l.contents_align,
371 ));
372
c6f2c051 373 ret.push_str("\n");
446cb3f1
DG
374}
375
c6f2c051 376fn print_handle(ret: &mut String, name: &Id, h: &HandleDatatype) {
e2bdce89 377 ret.push_str(&format!("typedef int __wasi_{}_t;\n\n", ident_name(name)));
c6f2c051
PH
378
379 ret.push_str(&format!(
380 "_Static_assert(sizeof(__wasi_{}_t) == {}, \"witx calculated size\");\n",
381 ident_name(name),
382 h.mem_size()
383 ));
384 ret.push_str(&format!(
385 "_Static_assert(_Alignof(__wasi_{}_t) == {}, \"witx calculated align\");\n",
386 ident_name(name),
387 h.mem_align()
388 ));
389
390 ret.push_str("\n");
446cb3f1
DG
391}
392
fe130532 393fn print_module(ret: &mut String, m: &Module) {
446cb3f1
DG
394 ret.push_str("/**\n");
395 ret.push_str(&format!(" * @defgroup {}\n", ident_name(&m.name),));
396 for line in m.docs.lines() {
397 ret.push_str(&format!(" * {}\n", line));
398 }
399 ret.push_str(" * @{\n");
400 ret.push_str(" */\n");
401 ret.push_str("\n");
402
403 for func in m.funcs() {
fe130532 404 print_func(ret, &func, &m.name);
446cb3f1
DG
405 }
406
407 ret.push_str("/** @} */\n");
408 ret.push_str("\n");
409}
410
fe130532 411fn print_func(ret: &mut String, func: &InterfaceFunc, module_name: &Id) {
446cb3f1
DG
412 if !func.docs.is_empty() {
413 ret.push_str("/**\n");
414 for line in func.docs.lines() {
415 ret.push_str(&format!(" * {}\n", line));
416 }
417 if !func.results.is_empty() {
418 let first_result = &func.results[0];
419 if !first_result.docs.is_empty() {
420 ret.push_str(" * @return\n");
421 for line in first_result.docs.lines() {
422 ret.push_str(&format!(" * {}", line));
423 }
424 }
425 }
426 ret.push_str(" */\n");
427 }
428 if func.results.is_empty() {
429 // Special-case "proc_exit" as _Noreturn -- TODO: Encode this in witx.
430 if func.name.as_str() == "proc_exit" {
431 ret.push_str("_Noreturn ");
432 }
433 ret.push_str("void ");
434 } else {
435 let first_result = &func.results[0];
fe130532 436 ret.push_str(&format!("{} ", typeref_name(&first_result.tref)));
446cb3f1
DG
437 }
438
439 ret.push_str(&format!("__wasi_{}(\n", ident_name(&func.name)));
440
441 if func.params.is_empty() && func.results.len() <= 1 {
442 ret.push_str(" void\n");
443 }
444 for (index, param) in func.params.iter().enumerate() {
445 if !param.docs.is_empty() {
446 ret.push_str(" /**\n");
447 for line in param.docs.lines() {
448 ret.push_str(&format!(" * {}\n", line));
449 }
450 ret.push_str(" */\n");
451 }
fe130532 452 add_params(ret, &ident_name(&param.name), &param.tref);
446cb3f1
DG
453 ret.push_str(&format!(
454 "{}\n",
455 if index + 1 < func.params.len() || func.results.len() > 1 {
456 ",\n"
457 } else {
458 ""
459 }
460 ));
461 }
462
463 for (index, result) in func.results.iter().enumerate() {
464 if index == 0 {
465 // The first result is returned by value above.
466 continue;
467 }
468 if !result.docs.is_empty() {
469 ret.push_str(" /**\n");
470 for line in result.docs.lines() {
471 ret.push_str(&format!(" * {}\n", line));
472 }
473 ret.push_str(" */\n");
474 }
475 ret.push_str(&format!(
476 " {} *{}{}\n",
fe130532 477 typeref_name(&result.tref),
446cb3f1
DG
478 ident_name(&result.name),
479 if index + 1 < func.results.len() {
480 ","
481 } else {
482 ""
483 }
484 ));
485 }
486
487 ret.push_str(") __attribute__((\n");
488 ret.push_str(&format!(
489 " __import_module__(\"{}\"),\n",
490 ident_name(module_name)
491 ));
492 ret.push_str(&format!(
493 " __import_name__(\"{}\")",
494 ident_name(&func.name)
495 ));
496 if !func.results.is_empty() {
497 ret.push_str(",\n __warn_unused_result__\n");
498 }
499 ret.push_str("));\n");
500 ret.push_str("\n");
501}
502
fe130532
PH
503fn add_params(ret: &mut String, name: &str, tref: &TypeRef) {
504 match &*tref.type_() {
505 Type::Builtin(BuiltinType::String) => {
446cb3f1
DG
506 ret.push_str(&format!(" const char *{},\n", name));
507 ret.push_str("\n");
508 ret.push_str(" /**\n");
509 ret.push_str(&format!(
510 " * The length of the buffer pointed to by `{}`.\n",
511 name,
512 ));
513 ret.push_str(" */\n");
514 ret.push_str(&format!(" size_t {}_len", name));
515 }
fe130532 516 Type::Array(element) => {
446cb3f1
DG
517 ret.push_str(&format!(
518 " const {} *{},\n",
fe130532 519 typeref_name(&element),
446cb3f1
DG
520 name
521 ));
522 ret.push_str("\n");
523 ret.push_str(" /**\n");
524 ret.push_str(&format!(
525 " * The length of the array pointed to by `{}`.\n",
526 name,
527 ));
528 ret.push_str(" */\n");
529 ret.push_str(&format!(" size_t {}_len", name));
530 }
531 _ => {
fe130532 532 ret.push_str(&format!(" {} {}", typeref_name(tref), name));
446cb3f1
DG
533 }
534 }
535}
536
537fn ident_name(i: &Id) -> String {
538 i.as_str().to_string()
539}
540
541fn builtin_type_name(b: BuiltinType) -> &'static str {
542 match b {
ec86d4de
PH
543 BuiltinType::String | BuiltinType::Char8 => {
544 panic!("no type name for string or char8 builtins")
545 }
546 BuiltinType::USize => "size_t",
446cb3f1
DG
547 BuiltinType::U8 => "uint8_t",
548 BuiltinType::U16 => "uint16_t",
549 BuiltinType::U32 => "uint32_t",
550 BuiltinType::U64 => "uint64_t",
551 BuiltinType::S8 => "int8_t",
552 BuiltinType::S16 => "int16_t",
553 BuiltinType::S32 => "int32_t",
554 BuiltinType::S64 => "int64_t",
555 BuiltinType::F32 => "float",
556 BuiltinType::F64 => "double",
557 }
558}
559
fe130532 560fn typeref_name(tref: &TypeRef) -> String {
ec86d4de
PH
561 match &*tref.type_() {
562 Type::Builtin(BuiltinType::String) | Type::Builtin(BuiltinType::Char8) | Type::Array(_) => {
563 panic!("unsupported grammar: cannot construct name of string or array",)
564 }
565 _ => {}
566 }
567
fe130532 568 match tref {
d415ae65 569 TypeRef::Name(named_type) => namedtype_name(&named_type),
fe130532 570 TypeRef::Value(anon_type) => match &**anon_type {
ec86d4de 571 Type::Array(_) => unreachable!("arrays excluded above"),
fe130532 572 Type::Builtin(b) => builtin_type_name(*b).to_string(),
fe130532
PH
573 Type::Pointer(p) => format!("{} *", typeref_name(&*p)),
574 Type::ConstPointer(p) => format!("const {} *", typeref_name(&*p)),
ec86d4de 575 Type::Int(i) => format!("{}", intrepr_name(i.repr)),
fe130532
PH
576 Type::Struct { .. }
577 | Type::Union { .. }
578 | Type::Enum { .. }
579 | Type::Flags { .. }
580 | Type::Handle { .. } => unreachable!(
581 "wasi should not have anonymous structs, unions, enums, flags, handles"
582 ),
583 },
446cb3f1
DG
584 }
585}
586
d415ae65
PH
587fn namedtype_name(named_type: &NamedType) -> String {
588 match &*named_type.type_() {
589 Type::Pointer(p) => format!("{} *", typeref_name(&*p)),
590 Type::ConstPointer(p) => format!("const {} *", typeref_name(&*p)),
591 Type::Array(_) => unreachable!("arrays excluded above"),
592 _ => format!("__wasi_{}_t", named_type.name.as_str()),
593 }
594}
595
446cb3f1
DG
596fn intrepr_name(i: IntRepr) -> &'static str {
597 match i {
598 IntRepr::U8 => "uint8_t",
599 IntRepr::U16 => "uint16_t",
600 IntRepr::U32 => "uint32_t",
601 IntRepr::U64 => "uint64_t",
602 }
603}
dd010bee
DG
604
605fn intrepr_const(i: IntRepr) -> &'static str {
606 match i {
607 IntRepr::U8 => "UINT8_C",
608 IntRepr::U16 => "UINT16_C",
609 IntRepr::U32 => "UINT32_C",
610 IntRepr::U64 => "UINT64_C",
611 }
612}