1use indexmap::IndexSet;
4use std::collections::{HashMap, HashSet};
5use wit_parser::{Resolve, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldItem};
6
7pub 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(¶m.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 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}