]>
Commit | Line | Data |
---|---|---|
446cb3f1 DG |
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(¶m.name), ¶m.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 | } |