1use crate::{
2 EngineOrModuleTypeIndex, EntityRef, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex,
3 ModuleTypes, TypeConvert, TypeIndex, WasmArrayType, WasmCompositeInnerType, WasmCompositeType,
4 WasmExnType, WasmFieldType, WasmFuncType, WasmHeapType, WasmResult, WasmStorageType,
5 WasmStructType, WasmSubType, wasm_unsupported,
6};
7use std::{
8 borrow::Cow,
9 collections::{HashMap, hash_map::Entry},
10 ops::Index,
11};
12use wasmparser::{UnpackedIndex, Validator, ValidatorId};
13use wasmtime_core::alloc::PanicOnOom as _;
14
15struct RecGroupStart {
21 rec_group_index: ModuleInternedRecGroupIndex,
22 start: ModuleInternedTypeIndex,
23 end: ModuleInternedTypeIndex,
24}
25
26pub struct ModuleTypesBuilder {
28 validator_id: ValidatorId,
36
37 types: ModuleTypes,
39
40 trampoline_types: HashMap<WasmFuncType, ModuleInternedTypeIndex>,
45
46 exception_types: HashMap<ModuleInternedTypeIndex, ModuleInternedTypeIndex>,
51
52 wasmparser_to_wasmtime: HashMap<wasmparser::types::CoreTypeId, ModuleInternedTypeIndex>,
55
56 already_seen: HashMap<wasmparser::types::RecGroupId, ModuleInternedRecGroupIndex>,
58
59 defining_rec_group: Option<RecGroupStart>,
62}
63
64impl ModuleTypesBuilder {
65 pub fn new(validator: &Validator) -> Self {
67 Self {
68 validator_id: validator.id(),
69 types: ModuleTypes::default(),
70 trampoline_types: HashMap::default(),
71 exception_types: HashMap::default(),
72 wasmparser_to_wasmtime: HashMap::default(),
73 already_seen: HashMap::default(),
74 defining_rec_group: None,
75 }
76 }
77
78 pub fn reserve_wasm_signatures(&mut self, amt: usize) {
80 self.types.reserve(amt);
81 self.wasmparser_to_wasmtime.reserve(amt);
82 self.already_seen.reserve(amt);
83 }
84
85 pub fn validator_id(&self) -> ValidatorId {
87 self.validator_id
88 }
89
90 pub fn intern_rec_group(
97 &mut self,
98 validator_types: wasmparser::types::TypesRef<'_>,
99 rec_group_id: wasmparser::types::RecGroupId,
100 ) -> WasmResult<ModuleInternedRecGroupIndex> {
101 assert_eq!(validator_types.id(), self.validator_id);
102
103 if let Some(interned) = self.already_seen.get(&rec_group_id) {
104 return Ok(*interned);
105 }
106
107 self.define_new_rec_group(validator_types, rec_group_id)
108 }
109
110 fn define_new_rec_group(
112 &mut self,
113 validator_types: wasmparser::types::TypesRef<'_>,
114 rec_group_id: wasmparser::types::RecGroupId,
115 ) -> WasmResult<ModuleInternedRecGroupIndex> {
116 assert_eq!(validator_types.id(), self.validator_id);
117
118 self.start_rec_group(
119 validator_types,
120 validator_types.rec_group_elements(rec_group_id),
121 );
122
123 for id in validator_types.rec_group_elements(rec_group_id) {
124 let ty = &validator_types[id];
125 let wasm_ty = WasmparserTypeConverter::new(self, |_| {
126 unreachable!("no need to lookup indexes; we already have core type IDs")
127 })
128 .with_rec_group(validator_types, rec_group_id)
129 .convert_sub_type(ty)?;
130 self.wasm_sub_type_in_rec_group(id, wasm_ty);
131 }
132
133 let rec_group_index = self.end_rec_group(rec_group_id);
134
135 for ty in self.rec_group_elements(rec_group_index) {
141 if self.types[ty].is_func() {
142 let trampoline = self.intern_trampoline_type(ty);
143 self.types.set_trampoline_type(ty, trampoline);
144 }
145 }
146
147 Ok(rec_group_index)
148 }
149
150 fn intern_trampoline_type(
153 &mut self,
154 for_func_ty: ModuleInternedTypeIndex,
155 ) -> ModuleInternedTypeIndex {
156 let sub_ty = &self.types[for_func_ty];
157 let trampoline = sub_ty.unwrap_func().trampoline_type().panic_on_oom();
158
159 if let Some(idx) = self.trampoline_types.get(&trampoline) {
160 *idx
162 } else {
163 match trampoline {
165 Cow::Borrowed(f) => {
170 self.trampoline_types.insert(f.clone(), for_func_ty);
171 for_func_ty
172 }
173 Cow::Owned(f) => {
177 let idx = self.types.push(WasmSubType {
178 is_final: true,
179 supertype: None,
180 composite_type: WasmCompositeType {
181 inner: WasmCompositeInnerType::Func(f.clone()),
182 shared: sub_ty.composite_type.shared,
183 },
184 });
185
186 self.types.set_trampoline_type(idx, idx);
188
189 let next = self.types.next_ty();
190 self.types.push_rec_group(idx..next);
191 self.trampoline_types.insert(f, idx);
192 idx
193 }
194 }
195 }
196 }
197
198 fn start_rec_group(
200 &mut self,
201 validator_types: wasmparser::types::TypesRef<'_>,
202 elems: impl ExactSizeIterator<Item = wasmparser::types::CoreTypeId>,
203 ) {
204 log::trace!("Starting rec group of length {}", elems.len());
205
206 assert!(self.defining_rec_group.is_none());
207 assert_eq!(validator_types.id(), self.validator_id);
208
209 let len = elems.len();
213 for (i, wasmparser_id) in elems.enumerate() {
214 let interned = ModuleInternedTypeIndex::new(self.types.len_types() + i);
215 log::trace!(
216 "Reserving {interned:?} for {wasmparser_id:?} = {:?}",
217 validator_types[wasmparser_id]
218 );
219
220 let old_entry = self.wasmparser_to_wasmtime.insert(wasmparser_id, interned);
221 debug_assert_eq!(
222 old_entry, None,
223 "should not have already inserted {wasmparser_id:?}"
224 );
225 }
226
227 self.defining_rec_group = Some(RecGroupStart {
228 rec_group_index: self.types.next_rec_group(),
229 start: self.types.next_ty(),
230 end: ModuleInternedTypeIndex::new(self.types.len_types() + len),
231 });
232 }
233
234 fn end_rec_group(
236 &mut self,
237 rec_group_id: wasmparser::types::RecGroupId,
238 ) -> ModuleInternedRecGroupIndex {
239 let RecGroupStart {
240 rec_group_index,
241 start,
242 end,
243 } = self
244 .defining_rec_group
245 .take()
246 .expect("should be defining a rec group");
247
248 log::trace!("Ending rec group {start:?}..{end:?}");
249
250 debug_assert!(start.index() < self.types.len_types());
251 debug_assert_eq!(
252 end,
253 self.types.next_ty(),
254 "should have defined the number of types declared in `start_rec_group`"
255 );
256
257 let idx = self.types.push_rec_group(start..end);
258 debug_assert_eq!(idx, rec_group_index);
259
260 self.already_seen.insert(rec_group_id, rec_group_index);
261 rec_group_index
262 }
263
264 pub fn intern_type(
271 &mut self,
272 validator_types: wasmparser::types::TypesRef<'_>,
273 id: wasmparser::types::CoreTypeId,
274 ) -> WasmResult<ModuleInternedTypeIndex> {
275 assert!(self.defining_rec_group.is_none());
276 assert_eq!(validator_types.id(), self.validator_id);
277
278 let rec_group_id = validator_types.rec_group_id_of(id);
279 debug_assert!(
280 validator_types
281 .rec_group_elements(rec_group_id)
282 .any(|e| e == id)
283 );
284
285 let interned_rec_group = self.intern_rec_group(validator_types, rec_group_id)?;
286
287 let interned_type = self.wasmparser_to_wasmtime[&id];
288 debug_assert!(
289 self.rec_group_elements(interned_rec_group)
290 .any(|e| e == interned_type)
291 );
292
293 Ok(interned_type)
294 }
295
296 fn wasm_sub_type_in_rec_group(
298 &mut self,
299 id: wasmparser::types::CoreTypeId,
300 ty: WasmSubType,
301 ) -> ModuleInternedTypeIndex {
302 assert!(
303 self.defining_rec_group.is_some(),
304 "must be defining a rec group to define new types"
305 );
306
307 let module_interned_index = self.types.push(ty);
308 debug_assert_eq!(
309 self.wasmparser_to_wasmtime.get(&id),
310 Some(&module_interned_index),
311 "should have reserved the right module-interned index for this wasmparser type already"
312 );
313
314 module_interned_index
315 }
316
317 pub fn define_exception_type_for_tag(
328 &mut self,
329 for_func_ty: ModuleInternedTypeIndex,
330 ) -> ModuleInternedTypeIndex {
331 match self.exception_types.entry(for_func_ty) {
332 Entry::Occupied(o) => *o.get(),
333 Entry::Vacant(v) => {
334 let fields = self.types[for_func_ty]
335 .unwrap_func()
336 .params()
337 .iter()
338 .map(|valtype| WasmFieldType {
339 element_type: WasmStorageType::Val(*valtype),
340 mutable: false,
341 })
342 .collect();
343 let idx = self.types.push(WasmSubType {
344 is_final: true,
345 supertype: None,
346 composite_type: WasmCompositeType {
347 inner: WasmCompositeInnerType::Exn(WasmExnType {
348 func_ty: EngineOrModuleTypeIndex::Module(for_func_ty),
349 fields,
350 }),
351 shared: false,
352 },
353 });
354 let next = self.types.next_ty();
355 self.types.push_rec_group(idx..next);
356 *v.insert(idx)
357 }
358 }
359 }
360
361 pub fn finish(self) -> ModuleTypes {
363 self.types
364 }
365
366 pub fn rec_group_elements(
368 &self,
369 rec_group: ModuleInternedRecGroupIndex,
370 ) -> impl ExactSizeIterator<Item = ModuleInternedTypeIndex> + use<> {
371 self.types.rec_group_elements(rec_group)
372 }
373
374 pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
377 self.types.wasm_types()
378 }
379
380 pub fn trampoline_types(
383 &self,
384 ) -> impl Iterator<Item = (ModuleInternedTypeIndex, ModuleInternedTypeIndex)> + '_ {
385 self.types.trampoline_types()
386 }
387
388 pub fn trampoline_type(&self, ty: ModuleInternedTypeIndex) -> ModuleInternedTypeIndex {
390 self.types.trampoline_type(ty)
391 }
392
393 pub fn unwrap_struct(&self, ty: ModuleInternedTypeIndex) -> WasmResult<&WasmStructType> {
403 let composite_type = &self.types[ty].composite_type;
404 if composite_type.shared {
405 return Err(wasm_unsupported!("shared structs are not yet implemented"));
406 }
407 Ok(composite_type.inner.unwrap_struct())
408 }
409
410 pub fn unwrap_array(&self, interned_ty: ModuleInternedTypeIndex) -> WasmResult<&WasmArrayType> {
420 let composite_type = &self.types[interned_ty].composite_type;
421 if composite_type.shared {
422 return Err(wasm_unsupported!("shared arrays are not yet implemented"));
423 }
424 Ok(composite_type.inner.unwrap_array())
425 }
426
427 pub fn unwrap_exn(&self, interned_ty: ModuleInternedTypeIndex) -> WasmResult<&WasmExnType> {
437 let composite_type = &self.types[interned_ty].composite_type;
438 if composite_type.shared {
439 return Err(wasm_unsupported!(
440 "shared exceptions are not yet implemented"
441 ));
442 }
443 Ok(composite_type.inner.unwrap_exn())
444 }
445
446 pub fn unwrap_func(&self, interned_ty: ModuleInternedTypeIndex) -> WasmResult<&WasmFuncType> {
456 let composite_type = &self.types[interned_ty].composite_type;
457 if composite_type.shared {
458 return Err(wasm_unsupported!(
459 "shared functions are not yet implemented"
460 ));
461 }
462 Ok(composite_type.inner.unwrap_func())
463 }
464}
465
466impl<T> Index<T> for ModuleTypesBuilder
468where
469 ModuleTypes: Index<T>,
470{
471 type Output = <ModuleTypes as Index<T>>::Output;
472
473 fn index(&self, sig: T) -> &Self::Output {
474 &self.types[sig]
475 }
476}
477
478pub struct WasmparserTypeConverter<'a, F> {
480 types: &'a ModuleTypesBuilder,
481 lookup_type_idx: F,
482 rec_group_context: Option<(
483 wasmparser::types::TypesRef<'a>,
484 wasmparser::types::RecGroupId,
485 )>,
486}
487
488impl<'a, F> WasmparserTypeConverter<'a, F> {
489 pub fn new(types: &'a ModuleTypesBuilder, lookup_type_idx: F) -> Self {
491 Self {
492 types,
493 lookup_type_idx,
494 rec_group_context: None,
495 }
496 }
497
498 pub fn with_rec_group(
501 &mut self,
502 wasmparser_types: wasmparser::types::TypesRef<'a>,
503 rec_group: wasmparser::types::RecGroupId,
504 ) -> &Self {
505 self.rec_group_context = Some((wasmparser_types, rec_group));
506 self
507 }
508}
509
510impl<F> TypeConvert for WasmparserTypeConverter<'_, F>
511where
512 F: Fn(TypeIndex) -> ModuleInternedTypeIndex,
513{
514 fn lookup_heap_type(&self, index: UnpackedIndex) -> WasmHeapType {
515 match index {
516 UnpackedIndex::Id(id) => {
517 let interned = self.types.wasmparser_to_wasmtime[&id];
518 let index = EngineOrModuleTypeIndex::Module(interned);
519
520 if let Some(ty) = self.types.types.get(interned) {
527 assert!(!ty.composite_type.shared);
528 match &ty.composite_type.inner {
529 WasmCompositeInnerType::Array(_) => WasmHeapType::ConcreteArray(index),
530 WasmCompositeInnerType::Func(_) => WasmHeapType::ConcreteFunc(index),
531 WasmCompositeInnerType::Struct(_) => WasmHeapType::ConcreteStruct(index),
532 WasmCompositeInnerType::Cont(_) => WasmHeapType::ConcreteCont(index),
533 WasmCompositeInnerType::Exn(_) => WasmHeapType::ConcreteExn(index),
534 }
535 } else if let Some((wasmparser_types, _)) = self.rec_group_context.as_ref() {
536 let wasmparser_ty = &wasmparser_types[id].composite_type;
537 assert!(!wasmparser_ty.shared);
538 match &wasmparser_ty.inner {
539 wasmparser::CompositeInnerType::Array(_) => {
540 WasmHeapType::ConcreteArray(index)
541 }
542 wasmparser::CompositeInnerType::Func(_) => {
543 WasmHeapType::ConcreteFunc(index)
544 }
545 wasmparser::CompositeInnerType::Struct(_) => {
546 WasmHeapType::ConcreteStruct(index)
547 }
548 wasmparser::CompositeInnerType::Cont(_) => {
549 WasmHeapType::ConcreteCont(index)
550 }
551 }
552 } else {
553 panic!("forward reference to type outside of rec group?")
554 }
555 }
556
557 UnpackedIndex::Module(module_index) => {
558 let module_index = TypeIndex::from_u32(module_index);
559 let interned = (self.lookup_type_idx)(module_index);
560 let index = EngineOrModuleTypeIndex::Module(interned);
561
562 if let Some(ty) = self.types.types.get(interned) {
568 assert!(!ty.composite_type.shared);
569 match &ty.composite_type.inner {
570 WasmCompositeInnerType::Array(_) => WasmHeapType::ConcreteArray(index),
571 WasmCompositeInnerType::Func(_) => WasmHeapType::ConcreteFunc(index),
572 WasmCompositeInnerType::Struct(_) => WasmHeapType::ConcreteStruct(index),
573 WasmCompositeInnerType::Cont(_) => WasmHeapType::ConcreteCont(index),
574 WasmCompositeInnerType::Exn(_) => WasmHeapType::ConcreteExn(index),
575 }
576 } else if let Some((parser_types, rec_group)) = self.rec_group_context.as_ref() {
577 let rec_group_index = interned.index() - self.types.types.len_types();
578 let id = parser_types
579 .rec_group_elements(*rec_group)
580 .nth(rec_group_index)
581 .unwrap();
582 let wasmparser_ty = &parser_types[id].composite_type;
583 assert!(!wasmparser_ty.shared);
584 match &wasmparser_ty.inner {
585 wasmparser::CompositeInnerType::Array(_) => {
586 WasmHeapType::ConcreteArray(index)
587 }
588 wasmparser::CompositeInnerType::Func(_) => {
589 WasmHeapType::ConcreteFunc(index)
590 }
591 wasmparser::CompositeInnerType::Struct(_) => {
592 WasmHeapType::ConcreteStruct(index)
593 }
594 wasmparser::CompositeInnerType::Cont(_) => {
595 WasmHeapType::ConcreteCont(index)
596 }
597 }
598 } else {
599 panic!("forward reference to type outside of rec group?")
600 }
601 }
602
603 UnpackedIndex::RecGroup(_) => unreachable!(),
604 }
605 }
606
607 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
608 match index {
609 UnpackedIndex::Id(id) => {
610 let interned = self.types.wasmparser_to_wasmtime[&id];
611 EngineOrModuleTypeIndex::Module(interned)
612 }
613 UnpackedIndex::Module(module_index) => {
614 let module_index = TypeIndex::from_u32(module_index);
615 let interned = (self.lookup_type_idx)(module_index);
616 EngineOrModuleTypeIndex::Module(interned)
617 }
618 UnpackedIndex::RecGroup(_) => unreachable!(),
619 }
620 }
621}