1use std::collections::{HashMap, HashSet};
2use std::io::{Read, Write};
3use std::mem;
4use std::process::Stdio;
5
6use anyhow::Result;
7use heck::ToSnakeCase;
8use wit_bindgen_c::imported_types_used_by_exported_interfaces;
9use wit_bindgen_core::wit_parser::{
10 Function, InterfaceId, LiveTypes, Resolve, SizeAlign, Type, TypeId, WorldId, WorldKey,
11};
12use wit_bindgen_core::{Direction, Files, Source, WorldGenerator};
13
14mod bindgen;
15mod imports;
16mod interface;
17
18#[derive(Debug, Clone)]
19#[cfg_attr(feature = "clap", derive(clap::Args))]
20pub struct Opts {
21 #[cfg_attr(feature = "clap", arg(long))]
23 pub gofmt: bool,
24
25 #[cfg_attr(feature = "clap", arg(long))]
27 pub rename_package: Option<String>,
28}
29
30impl Default for Opts {
31 fn default() -> Self {
32 Self {
33 gofmt: true,
34 rename_package: None,
35 } }
37}
38
39impl Opts {
40 pub fn build(&self) -> Box<dyn WorldGenerator> {
41 Box::new(TinyGo {
42 opts: self.clone(),
43 ..TinyGo::default()
44 })
45 }
46}
47
48#[derive(Default)]
49pub struct TinyGo {
50 opts: Opts,
51 src: Source,
52
53 preamble: Source,
55
56 world: String,
57
58 import_requirements: imports::ImportRequirements,
60
61 sizes: SizeAlign,
62
63 interface_names: HashMap<InterfaceId, WorldKey>,
65
66 c_type_names: HashMap<TypeId, String>,
68
69 c_type_namespaces: HashMap<TypeId, String>,
71
72 type_names: HashMap<TypeId, String>,
74
75 exported_resources: HashSet<TypeId>,
78
79 world_id: Option<WorldId>,
81}
82
83impl TinyGo {
84 fn interface<'a>(
85 &'a mut self,
86 resolve: &'a Resolve,
87 direction: Direction,
88 wasm_import_module: Option<&'a str>,
89 ) -> interface::InterfaceGenerator<'a> {
90 interface::InterfaceGenerator {
91 src: Source::default(),
92 preamble: Source::default(),
93 gen: self,
94 resolve,
95 interface: None,
96 direction,
97 export_funcs: Default::default(),
98 exported_resources: Default::default(),
99 methods: Default::default(),
100 wasm_import_module,
101 }
102 }
103
104 fn get_c_ty(&self, ty: &Type) -> String {
105 let res = match ty {
106 Type::Bool => "bool".into(),
107 Type::U8 => "uint8_t".into(),
108 Type::U16 => "uint16_t".into(),
109 Type::U32 => "uint32_t".into(),
110 Type::U64 => "uint64_t".into(),
111 Type::S8 => "int8_t".into(),
112 Type::S16 => "int16_t".into(),
113 Type::S32 => "int32_t".into(),
114 Type::S64 => "int64_t".into(),
115 Type::F32 => "float".into(),
116 Type::F64 => "double".into(),
117 Type::Char => "uint32_t".into(),
118 Type::ErrorContext => todo!(),
119 Type::String => {
120 format!(
121 "{namespace}_string_t",
122 namespace = self.world.to_snake_case()
123 )
124 }
125 Type::Id(id) => {
126 if let Some(name) = self.c_type_names.get(id) {
127 name.to_owned()
128 } else {
129 panic!("failed to find type name for {id:?}");
130 }
131 }
132 };
133 if res == "bool" {
134 return res;
135 }
136 format!("C.{res}")
137 }
138
139 fn with_result_option(&mut self, needs_result_option: bool) {
140 self.import_requirements.needs_result_option = needs_result_option;
141 }
142
143 fn with_import_unsafe(&mut self, needs_import_unsafe: bool) {
144 self.import_requirements.needs_import_unsafe = needs_import_unsafe;
145 }
146
147 fn with_fmt_import(&mut self, needs_fmt_import: bool) {
148 self.import_requirements.needs_fmt_import = needs_fmt_import;
149 }
150
151 pub fn with_sync_import(&mut self, needs_sync_import: bool) {
152 self.import_requirements.needs_sync_import = needs_sync_import;
153 }
154}
155
156impl WorldGenerator for TinyGo {
157 fn preprocess(&mut self, resolve: &Resolve, world: WorldId) {
158 self.world = self
159 .opts
160 .rename_package
161 .clone()
162 .unwrap_or_else(|| resolve.worlds[world].name.clone());
163 self.sizes.fill(resolve);
164 self.world_id = Some(world);
165 }
166
167 fn import_interface(
168 &mut self,
169 resolve: &Resolve,
170 name: &WorldKey,
171 id: InterfaceId,
172 _files: &mut Files,
173 ) -> Result<()> {
174 let name_raw = &resolve.name_world_key(name);
175 self.src
176 .push_str(&format!("// Import functions from {name_raw}\n"));
177 self.interface_names.insert(id, name.clone());
178
179 let mut gen = self.interface(resolve, Direction::Import, Some(name_raw));
180 gen.interface = Some((id, name));
181 gen.define_interface_types(id);
182
183 for (_name, func) in resolve.interfaces[id].functions.iter() {
184 gen.import(resolve, func);
185 }
186
187 let src = mem::take(&mut gen.src);
188 let preamble = mem::take(&mut gen.preamble);
189 self.src.push_str(&src);
190 self.preamble.append_src(&preamble);
191
192 Ok(())
193 }
194
195 fn import_funcs(
196 &mut self,
197 resolve: &Resolve,
198 world: WorldId,
199 funcs: &[(&str, &Function)],
200 _files: &mut Files,
201 ) {
202 let name = &resolve.worlds[world].name;
203 self.src
204 .push_str(&format!("// Import functions from {name}\n"));
205
206 let mut gen = self.interface(resolve, Direction::Import, Some("$root"));
207 gen.define_function_types(funcs);
208
209 for (_name, func) in funcs.iter() {
210 gen.import(resolve, func);
211 }
212 let src = mem::take(&mut gen.src);
213 let preamble = mem::take(&mut gen.preamble);
214 self.src.push_str(&src);
215 self.preamble.append_src(&preamble);
216 }
217
218 fn pre_export_interface(&mut self, resolve: &Resolve, _files: &mut Files) -> Result<()> {
219 let world = self.world_id.unwrap();
220 let live_import_types = imported_types_used_by_exported_interfaces(resolve, world);
221 self.c_type_namespaces
222 .retain(|k, _| live_import_types.contains(k));
223 self.c_type_names
224 .retain(|k, _| live_import_types.contains(k));
225 self.type_names.retain(|k, _| live_import_types.contains(k));
226 Ok(())
227 }
228
229 fn export_interface(
230 &mut self,
231 resolve: &Resolve,
232 name: &WorldKey,
233 id: InterfaceId,
234 _files: &mut Files,
235 ) -> Result<()> {
236 self.interface_names.insert(id, name.clone());
237 let name_raw = &resolve.name_world_key(name);
238 self.src
239 .push_str(&format!("// Export functions from {name_raw}\n"));
240
241 let mut gen = self.interface(resolve, Direction::Export, None);
242 gen.interface = Some((id, name));
243 gen.define_interface_types(id);
244
245 for (_name, func) in resolve.interfaces[id].functions.iter() {
246 gen.export(resolve, func);
247 }
248
249 gen.finish();
250
251 let src = mem::take(&mut gen.src);
252 let preamble = mem::take(&mut gen.preamble);
253 self.src.push_str(&src);
254 self.preamble.append_src(&preamble);
255 Ok(())
256 }
257
258 fn export_funcs(
259 &mut self,
260 resolve: &Resolve,
261 world: WorldId,
262 funcs: &[(&str, &Function)],
263 _files: &mut Files,
264 ) -> Result<()> {
265 let name = &resolve.worlds[world].name;
266 self.src
267 .push_str(&format!("// Export functions from {name}\n"));
268
269 let mut gen = self.interface(resolve, Direction::Export, None);
270 gen.define_function_types(funcs);
271
272 for (_name, func) in funcs.iter() {
273 gen.export(resolve, func);
274 }
275
276 gen.finish();
277
278 let src = mem::take(&mut gen.src);
279 let preamble = mem::take(&mut gen.preamble);
280 self.src.push_str(&src);
281 self.preamble.append_src(&preamble);
282 Ok(())
283 }
284
285 fn import_types(
286 &mut self,
287 resolve: &Resolve,
288 _world: WorldId,
289 types: &[(&str, TypeId)],
290 _files: &mut Files,
291 ) {
292 let mut gen = self.interface(resolve, Direction::Import, Some("$root"));
293 let mut live = LiveTypes::default();
294 for (_, id) in types {
295 live.add_type_id(resolve, *id);
296 }
297 gen.define_live_types(&live);
298 let src = mem::take(&mut gen.src);
299 self.src.push_str(&src);
300 }
301
302 fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> {
303 let src = mem::take(&mut self.src);
305 self.src.push_str(&src);
306
307 let src = mem::take(&mut self.src);
309 wit_bindgen_core::generated_preamble(&mut self.src, env!("CARGO_PKG_VERSION"));
310 let snake = avoid_keyword(self.world.to_snake_case().as_str()).to_owned();
311 self.src.push_str("package ");
313 self.src.push_str(&snake);
314 self.src.push_str("\n\n");
315
316 self.src.push_str("// #include \"");
318 self.src.push_str(self.world.to_snake_case().as_str());
319 self.src.push_str(".h\"\n");
320 self.src.push_str("// #include <stdlib.h>\n");
321 if self.preamble.len() > 0 {
322 self.src.append_src(&self.preamble);
323 }
324 self.src.push_str("import \"C\"\n");
325 let world = self.world.to_snake_case();
326
327 self.import_requirements
328 .generate(snake, files, format!("{}_types.go", world));
329 self.src.push_str(&self.import_requirements.src);
330
331 self.src.push_str(&src);
332
333 if self.opts.gofmt {
334 let mut child = std::process::Command::new("gofmt")
335 .stdin(Stdio::piped())
336 .stdout(Stdio::piped())
337 .spawn()
338 .expect("failed to spawn gofmt");
339 child
340 .stdin
341 .take()
342 .unwrap()
343 .write_all(self.src.as_bytes())
344 .expect("failed to write to gofmt");
345 self.src.as_mut_string().truncate(0);
346 child
347 .stdout
348 .take()
349 .unwrap()
350 .read_to_string(self.src.as_mut_string())
351 .expect("failed to read from gofmt");
352 let status = child.wait().expect("failed to wait on gofmt");
353 assert!(status.success());
354 }
355 files.push(&format!("{}.go", world), self.src.as_bytes());
356
357 let mut opts = wit_bindgen_c::Opts::default();
358 opts.no_sig_flattening = true;
359 opts.no_object_file = true;
360 opts.rename_world = self.opts.rename_package.clone();
361 opts.build()
362 .generate(resolve, id, files)
363 .expect("C generator should be infallible");
364
365 Ok(())
366 }
367}
368
369fn avoid_keyword(s: &str) -> String {
370 if GOKEYWORDS.contains(&s) {
371 format!("_{s}")
372 } else {
373 s.into()
374 }
375}
376
377const GOKEYWORDS: [&str; 26] = [
379 "break",
380 "default",
381 "func",
382 "interface",
383 "select",
384 "case",
385 "defer",
386 "go",
387 "map",
388 "struct",
389 "chan",
390 "else",
391 "goto",
392 "package",
393 "switch",
394 "const",
395 "fallthrough",
396 "if",
397 "range",
398 "type",
399 "continue",
400 "for",
401 "import",
402 "return",
403 "var",
404 "ret",
407];