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 indexmap::IndexSet;
4use std::collections::{HashMap, HashSet};
5use wit_parser::{Resolve, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldItem};
6
7/// Generate a JS shim from WIT metadata that sets up stream/future factories.
8pub fn generate_shim(resolve: &Resolve, world_id: WorldId) -> String {
9    let mut ctx = EmitContext::new(resolve, world_id);
10    ctx.emit();
11    ctx.output()
12}
13
14struct EmitContext<'a> {
15    resolve: &'a Resolve,
16    world_id: WorldId,
17    lines: Vec<String>,
18    streams: IndexSet<Option<Type>>,
19    futures: IndexSet<Option<Type>>,
20    visited_types: HashSet<TypeId>,
21}
22
23impl<'a> EmitContext<'a> {
24    fn new(resolve: &'a Resolve, world_id: WorldId) -> Self {
25        Self {
26            resolve,
27            world_id,
28            lines: Vec::new(),
29            streams: IndexSet::new(),
30            futures: IndexSet::new(),
31            visited_types: HashSet::new(),
32        }
33    }
34
35    fn line(&mut self, s: &str) {
36        self.lines.push(s.to_string());
37    }
38
39    fn output(self) -> String {
40        self.lines.join("\n") + "\n"
41    }
42
43    fn emit(&mut self) {
44        let world = &self.resolve.worlds[self.world_id];
45
46        for item in world.imports.values() {
47            self.collect_from_world_item(item);
48        }
49
50        for item in world.exports.values() {
51            if let WorldItem::Function(f) = item {
52                self.collect_from_function(f);
53            }
54        }
55
56        for item in world.exports.values() {
57            if let WorldItem::Interface { id, .. } = item {
58                for f in self.resolve.interfaces[*id].functions.values() {
59                    self.collect_from_function(f);
60                }
61            }
62        }
63
64        self.line("const wit = globalThis.wit = {};");
65
66        let streams: Vec<_> = self.streams.iter().copied().collect();
67        if !streams.is_empty() {
68            self.emit_constructor("Stream", "__cqjs.makeStream", &streams);
69        }
70
71        let futures: Vec<_> = self.futures.iter().copied().collect();
72        if !futures.is_empty() {
73            self.emit_constructor("Future", "__cqjs.makeFuture", &futures);
74        }
75    }
76
77    fn emit_constructor(&mut self, name: &str, native_fn: &str, types: &[Option<Type>]) {
78        if types.len() == 1 {
79            self.line(&format!(
80                "wit.{name} = function(type) {{ return {native_fn}(type ?? 0); }};"
81            ));
82        } else {
83            self.line(&format!("wit.{name} = function(type) {{"));
84            self.line(&format!(
85                "  if (type === undefined) throw new Error('{name} type required, use wit.{name}.<TYPE>');"
86            ));
87            self.line(&format!("  return {native_fn}(type);"));
88            self.line("};");
89        }
90
91        self.line(&format!("wit.{name}.types = {{}};"));
92        for (index, const_name) in unique_const_names(self.resolve, types)
93            .into_iter()
94            .enumerate()
95        {
96            self.line(&format!(
97                "wit.{name}.{const_name} = {index}; wit.{name}.types.{const_name} = {index};"
98            ));
99        }
100    }
101
102    fn collect_from_world_item(&mut self, item: &WorldItem) {
103        match item {
104            WorldItem::Function(f) => {
105                self.collect_from_function(f);
106            }
107            WorldItem::Interface { id, .. } => {
108                for f in self.resolve.interfaces[*id].functions.values() {
109                    self.collect_from_function(f);
110                }
111            }
112            WorldItem::Type { id, .. } => {
113                self.collect_from_type_id(*id);
114            }
115        }
116    }
117
118    fn collect_from_function(&mut self, func: &wit_parser::Function) {
119        for param in &func.params {
120            self.collect_from_type(&param.ty);
121        }
122        if let Some(result) = &func.result {
123            self.collect_from_type(result);
124        }
125    }
126
127    fn collect_from_type(&mut self, ty: &Type) {
128        if let Type::Id(id) = ty {
129            self.collect_from_type_id(*id);
130        }
131    }
132
133    fn collect_from_type_id(&mut self, id: TypeId) {
134        if !self.visited_types.insert(id) {
135            return;
136        }
137
138        let typedef = &self.resolve.types[id];
139        match &typedef.kind {
140            TypeDefKind::Stream(elem) => {
141                if let Some(elem) = elem {
142                    self.collect_from_type(elem);
143                }
144                self.streams.insert(*elem);
145            }
146            TypeDefKind::Future(elem) => {
147                if let Some(elem) = elem {
148                    self.collect_from_type(elem);
149                }
150                self.futures.insert(*elem);
151            }
152            TypeDefKind::Record(r) => {
153                let tys: Vec<_> = r.fields.iter().map(|f| f.ty).collect();
154                for ty in &tys {
155                    self.collect_from_type(ty);
156                }
157            }
158            TypeDefKind::Tuple(t) => {
159                let tys = t.types.clone();
160                for ty in &tys {
161                    self.collect_from_type(ty);
162                }
163            }
164            TypeDefKind::Variant(v) => {
165                let tys: Vec<_> = v.cases.iter().filter_map(|c| c.ty).collect();
166                for ty in &tys {
167                    self.collect_from_type(ty);
168                }
169            }
170            TypeDefKind::Option(ty) => {
171                let ty = *ty;
172                self.collect_from_type(&ty);
173            }
174            TypeDefKind::Result(r) => {
175                let ok = r.ok;
176                let err = r.err;
177                if let Some(ty) = &ok {
178                    self.collect_from_type(ty);
179                }
180                if let Some(ty) = &err {
181                    self.collect_from_type(ty);
182                }
183            }
184            TypeDefKind::List(ty) => {
185                let ty = *ty;
186                self.collect_from_type(&ty);
187            }
188            TypeDefKind::Type(ty) => {
189                let ty = *ty;
190                self.collect_from_type(&ty);
191            }
192            _ => {}
193        }
194    }
195}
196
197#[derive(Clone, Copy)]
198enum ConstNameStyle {
199    Local,
200    Qualified,
201}
202
203fn type_const_name(resolve: &Resolve, ty: Option<&Type>, style: ConstNameStyle) -> String {
204    match ty {
205        None => "UNIT".to_string(),
206        Some(Type::Bool) => "BOOL".to_string(),
207        Some(Type::U8) => "U8".to_string(),
208        Some(Type::S8) => "S8".to_string(),
209        Some(Type::U16) => "U16".to_string(),
210        Some(Type::S16) => "S16".to_string(),
211        Some(Type::U32) => "U32".to_string(),
212        Some(Type::S32) => "S32".to_string(),
213        Some(Type::U64) => "U64".to_string(),
214        Some(Type::S64) => "S64".to_string(),
215        Some(Type::F32) => "F32".to_string(),
216        Some(Type::F64) => "F64".to_string(),
217        Some(Type::Char) => "CHAR".to_string(),
218        Some(Type::String) => "STRING".to_string(),
219        Some(Type::ErrorContext) => "ERROR_CONTEXT".to_string(),
220        Some(Type::Id(id)) => typedef_const_name(resolve, *id, style),
221    }
222}
223
224fn typedef_const_name(resolve: &Resolve, id: TypeId, style: ConstNameStyle) -> String {
225    let typedef = &resolve.types[id];
226
227    if let Some(name) = &typedef.name {
228        return match style {
229            ConstNameStyle::Local => const_ident(name),
230            ConstNameStyle::Qualified => {
231                let prefix = match typedef.owner {
232                    TypeOwner::Interface(interface) => resolve.id_of(interface),
233                    TypeOwner::World(world) => Some(resolve.worlds[world].name.clone()),
234                    TypeOwner::None => None,
235                };
236
237                match prefix {
238                    Some(prefix) => const_ident(&format!("{prefix}-{name}")),
239                    None => const_ident(name),
240                }
241            }
242        };
243    }
244
245    // Build type name recursively, e.g. OPTION_U32, RESULT_STRING_VOID, etc.
246    match &typedef.kind {
247        TypeDefKind::Option(inner) => {
248            format!("OPTION_{}", type_const_name(resolve, Some(inner), style))
249        }
250        TypeDefKind::Tuple(t) => {
251            let inner: Vec<String> = t
252                .types
253                .iter()
254                .map(|t| type_const_name(resolve, Some(t), style))
255                .collect();
256            format!("TUPLE_{}", inner.join("_"))
257        }
258        TypeDefKind::Result(r) => {
259            let ok =
260                r.ok.as_ref()
261                    .map(|t| type_const_name(resolve, Some(t), style))
262                    .unwrap_or("VOID".to_string());
263            let err = r
264                .err
265                .as_ref()
266                .map(|t| type_const_name(resolve, Some(t), style))
267                .unwrap_or("VOID".to_string());
268            format!("RESULT_{ok}_{err}")
269        }
270        TypeDefKind::List(inner) => {
271            format!("LIST_{}", type_const_name(resolve, Some(inner), style))
272        }
273        TypeDefKind::Future(inner) => {
274            let inner = inner
275                .as_ref()
276                .map(|t| type_const_name(resolve, Some(t), style))
277                .unwrap_or("UNIT".to_string());
278            format!("FUTURE_{inner}")
279        }
280        TypeDefKind::Stream(inner) => {
281            let inner = inner
282                .as_ref()
283                .map(|t| type_const_name(resolve, Some(t), style))
284                .unwrap_or("UNIT".to_string());
285            format!("STREAM_{inner}")
286        }
287        TypeDefKind::Type(inner) => type_const_name(resolve, Some(inner), style),
288        _ => "OTHER".to_string(),
289    }
290}
291
292fn unique_const_names(resolve: &Resolve, types: &[Option<Type>]) -> Vec<String> {
293    let base_names: Vec<_> = types
294        .iter()
295        .map(|ty| type_const_name(resolve, ty.as_ref(), ConstNameStyle::Local))
296        .collect();
297    let mut counts = HashMap::<String, usize>::new();
298    for name in &base_names {
299        *counts.entry(name.clone()).or_default() += 1;
300    }
301
302    let mut used = HashSet::new();
303    base_names
304        .into_iter()
305        .zip(types.iter())
306        .map(|(base, ty)| {
307            let candidate = if counts[base.as_str()] > 1 {
308                type_const_name(resolve, ty.as_ref(), ConstNameStyle::Qualified)
309            } else {
310                base
311            };
312
313            unique_name(candidate, &mut used)
314        })
315        .collect()
316}
317
318fn unique_name(candidate: String, used: &mut HashSet<String>) -> String {
319    if used.insert(candidate.clone()) {
320        return candidate;
321    }
322
323    let mut suffix = 2;
324    loop {
325        let name = format!("{candidate}_{suffix}");
326        if used.insert(name.clone()) {
327            return name;
328        }
329        suffix += 1;
330    }
331}
332
333fn const_ident(s: &str) -> String {
334    let mut out = String::new();
335    for c in s.chars() {
336        if c.is_ascii_alphanumeric() {
337            out.push(c.to_ascii_uppercase());
338        } else {
339            out.push('_');
340        }
341    }
342
343    if out.as_bytes().first().is_some_and(|b| b.is_ascii_digit()) {
344        out.insert(0, '_');
345    }
346
347    out
348}