Skip to main content

componentize_qjs/
codegen.rs

1//! Code generation for the JS shim that bridges WIT types to the quickjs runtime.
2
3use std::collections::HashSet;
4use wit_parser::{Resolve, Type, TypeDefKind, TypeId, WorldId, WorldItem};
5
6/// Generate a JS shim from WIT metadata that sets up stream/future factories.
7pub fn generate_shim(resolve: &Resolve, world_id: WorldId) -> String {
8    let mut ctx = EmitContext::new(resolve, world_id);
9    ctx.emit();
10    ctx.output()
11}
12
13struct EmitContext<'a> {
14    resolve: &'a Resolve,
15    world_id: WorldId,
16    lines: Vec<String>,
17    streams: HashSet<Option<Type>>,
18    futures: HashSet<Option<Type>>,
19}
20
21impl<'a> EmitContext<'a> {
22    fn new(resolve: &'a Resolve, world_id: WorldId) -> Self {
23        Self {
24            resolve,
25            world_id,
26            lines: Vec::new(),
27            streams: HashSet::new(),
28            futures: HashSet::new(),
29        }
30    }
31
32    fn line(&mut self, s: &str) {
33        self.lines.push(s.to_string());
34    }
35
36    fn output(self) -> String {
37        self.lines.join("\n") + "\n"
38    }
39
40    fn emit(&mut self) {
41        let world = &self.resolve.worlds[self.world_id];
42
43        for item in world.imports.values().chain(world.exports.values()) {
44            match item {
45                WorldItem::Function(f) => {
46                    self.collect_from_function(f);
47                }
48                WorldItem::Interface { id, .. } => {
49                    for f in self.resolve.interfaces[*id].functions.values() {
50                        self.collect_from_function(f);
51                    }
52                }
53                WorldItem::Type(id) => {
54                    self.collect_from_type_id(*id);
55                }
56            }
57        }
58
59        self.line("globalThis.wit = {};");
60
61        let streams: Vec<_> = self.streams.iter().copied().collect();
62        if !streams.is_empty() {
63            self.emit_constructor("Stream", "__cqjs.makeStream", &streams);
64        }
65
66        let futures: Vec<_> = self.futures.iter().copied().collect();
67        if !futures.is_empty() {
68            self.emit_constructor("Future", "__cqjs.makeFuture", &futures);
69        }
70    }
71
72    fn emit_constructor(&mut self, name: &str, native_fn: &str, types: &[Option<Type>]) {
73        if types.len() == 1 {
74            self.line(&format!(
75                "wit.{name} = function(type) {{ return {native_fn}(type ?? 0); }};"
76            ));
77        } else {
78            self.line(&format!("wit.{name} = function(type) {{"));
79            self.line(&format!(
80                "  if (type === undefined) throw new Error('{name} type required, use wit.{name}.<TYPE>');"
81            ));
82            self.line(&format!("  return {native_fn}(type);"));
83            self.line("};");
84        }
85
86        self.line(&format!("wit.{name}.types = {{}};"));
87        for (index, elem_ty) in types.iter().enumerate() {
88            let const_name = type_const_name(self.resolve, elem_ty.as_ref());
89            self.line(&format!(
90                "wit.{name}.{const_name} = {index}; wit.{name}.types.{const_name} = {index};"
91            ));
92        }
93    }
94
95    fn collect_from_function(&mut self, func: &wit_parser::Function) {
96        for (_, ty) in &func.params {
97            self.collect_from_type(ty);
98        }
99        if let Some(result) = &func.result {
100            self.collect_from_type(result);
101        }
102    }
103
104    fn collect_from_type(&mut self, ty: &Type) {
105        if let Type::Id(id) = ty {
106            self.collect_from_type_id(*id);
107        }
108    }
109
110    fn collect_from_type_id(&mut self, id: TypeId) {
111        let typedef = &self.resolve.types[id];
112        match &typedef.kind {
113            TypeDefKind::Stream(elem) => {
114                self.streams.insert(*elem);
115                if let Some(elem) = elem {
116                    self.collect_from_type(elem);
117                }
118            }
119            TypeDefKind::Future(elem) => {
120                self.futures.insert(*elem);
121                if let Some(elem) = elem {
122                    self.collect_from_type(elem);
123                }
124            }
125            TypeDefKind::Record(r) => {
126                let tys: Vec<_> = r.fields.iter().map(|f| f.ty).collect();
127                for ty in &tys {
128                    self.collect_from_type(ty);
129                }
130            }
131            TypeDefKind::Tuple(t) => {
132                let tys = t.types.clone();
133                for ty in &tys {
134                    self.collect_from_type(ty);
135                }
136            }
137            TypeDefKind::Variant(v) => {
138                let tys: Vec<_> = v.cases.iter().filter_map(|c| c.ty).collect();
139                for ty in &tys {
140                    self.collect_from_type(ty);
141                }
142            }
143            TypeDefKind::Option(ty) => {
144                let ty = *ty;
145                self.collect_from_type(&ty);
146            }
147            TypeDefKind::Result(r) => {
148                let ok = r.ok;
149                let err = r.err;
150                if let Some(ty) = &ok {
151                    self.collect_from_type(ty);
152                }
153                if let Some(ty) = &err {
154                    self.collect_from_type(ty);
155                }
156            }
157            TypeDefKind::List(ty) => {
158                let ty = *ty;
159                self.collect_from_type(&ty);
160            }
161            TypeDefKind::Type(ty) => {
162                let ty = *ty;
163                self.collect_from_type(&ty);
164            }
165            _ => {}
166        }
167    }
168}
169
170fn type_const_name(resolve: &Resolve, ty: Option<&Type>) -> String {
171    match ty {
172        None => "UNIT".to_string(),
173        Some(Type::Bool) => "BOOL".to_string(),
174        Some(Type::U8) => "U8".to_string(),
175        Some(Type::S8) => "S8".to_string(),
176        Some(Type::U16) => "U16".to_string(),
177        Some(Type::S16) => "S16".to_string(),
178        Some(Type::U32) => "U32".to_string(),
179        Some(Type::S32) => "S32".to_string(),
180        Some(Type::U64) => "U64".to_string(),
181        Some(Type::S64) => "S64".to_string(),
182        Some(Type::F32) => "F32".to_string(),
183        Some(Type::F64) => "F64".to_string(),
184        Some(Type::Char) => "CHAR".to_string(),
185        Some(Type::String) => "STRING".to_string(),
186        Some(Type::ErrorContext) => "ERROR_CONTEXT".to_string(),
187        Some(Type::Id(id)) => typedef_const_name(resolve, *id),
188    }
189}
190
191fn typedef_const_name(resolve: &Resolve, id: TypeId) -> String {
192    let typedef = &resolve.types[id];
193
194    if let Some(name) = &typedef.name {
195        return name.to_uppercase().replace('-', "_");
196    }
197
198    // Build type name recursively, e.g. OPTION_U32, RESULT_STRING_VOID, etc.
199    match &typedef.kind {
200        TypeDefKind::Option(inner) => {
201            format!("OPTION_{}", type_const_name(resolve, Some(inner)))
202        }
203        TypeDefKind::Tuple(t) => {
204            let inner: Vec<String> = t
205                .types
206                .iter()
207                .map(|t| type_const_name(resolve, Some(t)))
208                .collect();
209            format!("TUPLE_{}", inner.join("_"))
210        }
211        TypeDefKind::Result(r) => {
212            let ok =
213                r.ok.as_ref()
214                    .map(|t| type_const_name(resolve, Some(t)))
215                    .unwrap_or("VOID".to_string());
216            let err = r
217                .err
218                .as_ref()
219                .map(|t| type_const_name(resolve, Some(t)))
220                .unwrap_or("VOID".to_string());
221            format!("RESULT_{ok}_{err}")
222        }
223        TypeDefKind::List(inner) => {
224            format!("LIST_{}", type_const_name(resolve, Some(inner)))
225        }
226        TypeDefKind::Type(inner) => type_const_name(resolve, Some(inner)),
227        _ => "OTHER".to_string(),
228    }
229}