wit_bindgen_go/
lib.rs

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    /// Whether or not `gofmt` is executed to format generated code.
22    #[cfg_attr(feature = "clap", arg(long))]
23    pub gofmt: bool,
24
25    /// Rename the Go package in the generated source code.
26    #[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        } // Set the default value of gofmt to true
36    }
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    // the parts immediately precede the import of "C"
54    preamble: Source,
55
56    world: String,
57
58    // import requirements for the generated code
59    import_requirements: imports::ImportRequirements,
60
61    sizes: SizeAlign,
62
63    // mapping from interface ID to the name of the interface
64    interface_names: HashMap<InterfaceId, WorldKey>,
65
66    // C type names
67    c_type_names: HashMap<TypeId, String>,
68
69    // C type namespaces
70    c_type_namespaces: HashMap<TypeId, String>,
71
72    // Go type names
73    type_names: HashMap<TypeId, String>,
74
75    // tracking all the exported resources used in generating the
76    // resource interface and the resource destructors
77    exported_resources: HashSet<TypeId>,
78
79    // the world ID
80    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        // make sure all types are defined on top of the file
304        let src = mem::take(&mut self.src);
305        self.src.push_str(&src);
306
307        // prepend package and imports header
308        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        // add package
312        self.src.push_str("package ");
313        self.src.push_str(&snake);
314        self.src.push_str("\n\n");
315
316        // import C
317        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
377// a list of Go keywords
378const 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    // not a Go keyword but needs to escape due to
405    // it's used as a variable name that passes to C
406    "ret",
407];