1 | //! State relating to validating a WebAssembly module. |
2 | //! |
3 | |
4 | mod canonical; |
5 | pub(crate) use canonical::InternRecGroup; |
6 | |
7 | use self::arc::MaybeOwned; |
8 | use super::{ |
9 | check_max, combine_type_sizes, |
10 | operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations}, |
11 | types::{CoreTypeId, EntityType, RecGroupId, TypeAlloc, TypeList}, |
12 | }; |
13 | #[cfg (feature = "simd" )] |
14 | use crate::VisitSimdOperator; |
15 | use crate::{ |
16 | limits::*, BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, |
17 | FuncType, Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, SubType, Table, |
18 | TableInit, TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, WasmFeatures, |
19 | WasmModuleResources, |
20 | }; |
21 | use crate::{prelude::*, CompositeInnerType}; |
22 | use alloc::sync::Arc; |
23 | use core::mem; |
24 | |
25 | // Section order for WebAssembly modules. |
26 | // |
27 | // Component sections are unordered and allow for duplicates, |
28 | // so this isn't used for components. |
29 | #[derive (Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Debug)] |
30 | pub enum Order { |
31 | #[default] |
32 | Initial, |
33 | Type, |
34 | Import, |
35 | Function, |
36 | Table, |
37 | Memory, |
38 | Tag, |
39 | Global, |
40 | Export, |
41 | Start, |
42 | Element, |
43 | DataCount, |
44 | Code, |
45 | Data, |
46 | } |
47 | |
48 | #[derive (Default)] |
49 | pub(crate) struct ModuleState { |
50 | /// Internal state that is incrementally built-up for the module being |
51 | /// validated. This houses type information for all wasm items, like |
52 | /// functions. Note that this starts out as a solely owned `Arc<T>` so we can |
53 | /// get mutable access, but after we get to the code section this is never |
54 | /// mutated to we can clone it cheaply and hand it to sub-validators. |
55 | pub module: arc::MaybeOwned<Module>, |
56 | |
57 | /// Where we are, order-wise, in the wasm binary. |
58 | order: Order, |
59 | |
60 | /// The number of data segments in the data section (if present). |
61 | pub data_segment_count: u32, |
62 | |
63 | /// The number of functions we expect to be defined in the code section, or |
64 | /// basically the length of the function section if it was found. The next |
65 | /// index is where we are, in the code section index space, for the next |
66 | /// entry in the code section (used to figure out what type is next for the |
67 | /// function being validated). |
68 | pub expected_code_bodies: Option<u32>, |
69 | |
70 | const_expr_allocs: OperatorValidatorAllocations, |
71 | |
72 | /// When parsing the code section, represents the current index in the section. |
73 | code_section_index: Option<usize>, |
74 | } |
75 | |
76 | impl ModuleState { |
77 | pub fn update_order(&mut self, order: Order, offset: usize) -> Result<()> { |
78 | if self.order >= order { |
79 | return Err(BinaryReaderError::new("section out of order" , offset)); |
80 | } |
81 | |
82 | self.order = order; |
83 | |
84 | Ok(()) |
85 | } |
86 | |
87 | pub fn validate_end(&self, offset: usize) -> Result<()> { |
88 | // Ensure that the data count section, if any, was correct. |
89 | if let Some(data_count) = self.module.data_count { |
90 | if data_count != self.data_segment_count { |
91 | return Err(BinaryReaderError::new( |
92 | "data count and data section have inconsistent lengths" , |
93 | offset, |
94 | )); |
95 | } |
96 | } |
97 | // Ensure that the function section, if nonzero, was paired with a code |
98 | // section with the appropriate length. |
99 | if let Some(n) = self.expected_code_bodies { |
100 | if n > 0 { |
101 | return Err(BinaryReaderError::new( |
102 | "function and code section have inconsistent lengths" , |
103 | offset, |
104 | )); |
105 | } |
106 | } |
107 | |
108 | Ok(()) |
109 | } |
110 | |
111 | pub fn next_code_index_and_type(&mut self, offset: usize) -> Result<(u32, u32)> { |
112 | let index = self |
113 | .code_section_index |
114 | .get_or_insert(self.module.num_imported_functions as usize); |
115 | |
116 | if *index >= self.module.functions.len() { |
117 | return Err(BinaryReaderError::new( |
118 | "code section entry exceeds number of functions" , |
119 | offset, |
120 | )); |
121 | } |
122 | |
123 | let ty = self.module.functions[*index]; |
124 | *index += 1; |
125 | |
126 | Ok(((*index - 1) as u32, ty)) |
127 | } |
128 | |
129 | pub fn add_global( |
130 | &mut self, |
131 | mut global: Global, |
132 | features: &WasmFeatures, |
133 | types: &TypeList, |
134 | offset: usize, |
135 | ) -> Result<()> { |
136 | self.module |
137 | .check_global_type(&mut global.ty, features, types, offset)?; |
138 | self.check_const_expr(&global.init_expr, global.ty.content_type, features, types)?; |
139 | self.module.assert_mut().globals.push(global.ty); |
140 | Ok(()) |
141 | } |
142 | |
143 | pub fn add_table( |
144 | &mut self, |
145 | mut table: Table<'_>, |
146 | features: &WasmFeatures, |
147 | types: &TypeList, |
148 | offset: usize, |
149 | ) -> Result<()> { |
150 | self.module |
151 | .check_table_type(&mut table.ty, features, types, offset)?; |
152 | |
153 | match &table.init { |
154 | TableInit::RefNull => { |
155 | if !table.ty.element_type.is_nullable() { |
156 | bail!(offset, "type mismatch: non-defaultable element type" ); |
157 | } |
158 | } |
159 | TableInit::Expr(expr) => { |
160 | if !features.function_references() { |
161 | bail!( |
162 | offset, |
163 | "tables with expression initializers require \ |
164 | the function-references proposal" |
165 | ); |
166 | } |
167 | self.check_const_expr(expr, table.ty.element_type.into(), features, types)?; |
168 | } |
169 | } |
170 | self.module.assert_mut().tables.push(table.ty); |
171 | Ok(()) |
172 | } |
173 | |
174 | pub fn add_data_segment( |
175 | &mut self, |
176 | data: Data, |
177 | features: &WasmFeatures, |
178 | types: &TypeList, |
179 | offset: usize, |
180 | ) -> Result<()> { |
181 | match data.kind { |
182 | DataKind::Passive => Ok(()), |
183 | DataKind::Active { |
184 | memory_index, |
185 | offset_expr, |
186 | } => { |
187 | let ty = self.module.memory_at(memory_index, offset)?.index_type(); |
188 | self.check_const_expr(&offset_expr, ty, features, types) |
189 | } |
190 | } |
191 | } |
192 | |
193 | pub fn add_element_segment( |
194 | &mut self, |
195 | mut e: Element, |
196 | features: &WasmFeatures, |
197 | types: &TypeList, |
198 | offset: usize, |
199 | ) -> Result<()> { |
200 | // the `funcref` value type is allowed all the way back to the MVP, so |
201 | // don't check it here |
202 | let element_ty = match &mut e.items { |
203 | crate::ElementItems::Functions(_) => RefType::FUNC, |
204 | crate::ElementItems::Expressions(ty, _) => { |
205 | self.module.check_ref_type(ty, features, offset)?; |
206 | *ty |
207 | } |
208 | }; |
209 | |
210 | match e.kind { |
211 | ElementKind::Active { |
212 | table_index, |
213 | offset_expr, |
214 | } => { |
215 | let table = self.module.table_at(table_index.unwrap_or(0), offset)?; |
216 | if !types.reftype_is_subtype(element_ty, table.element_type) { |
217 | return Err(BinaryReaderError::new( |
218 | format!( |
219 | "type mismatch: invalid element type ` {}` for table type ` {}`" , |
220 | ty_to_str(element_ty.into()), |
221 | ty_to_str(table.element_type.into()), |
222 | ), |
223 | offset, |
224 | )); |
225 | } |
226 | |
227 | self.check_const_expr(&offset_expr, table.index_type(), features, types)?; |
228 | } |
229 | ElementKind::Passive | ElementKind::Declared => { |
230 | if !features.bulk_memory() { |
231 | return Err(BinaryReaderError::new( |
232 | "bulk memory must be enabled" , |
233 | offset, |
234 | )); |
235 | } |
236 | } |
237 | } |
238 | |
239 | let validate_count = |count: u32| -> Result<(), BinaryReaderError> { |
240 | if count > MAX_WASM_TABLE_ENTRIES as u32 { |
241 | Err(BinaryReaderError::new( |
242 | "number of elements is out of bounds" , |
243 | offset, |
244 | )) |
245 | } else { |
246 | Ok(()) |
247 | } |
248 | }; |
249 | match e.items { |
250 | crate::ElementItems::Functions(reader) => { |
251 | let count = reader.count(); |
252 | validate_count(count)?; |
253 | for f in reader.into_iter_with_offsets() { |
254 | let (offset, f) = f?; |
255 | self.module.get_func_type(f, types, offset)?; |
256 | self.module.assert_mut().function_references.insert(f); |
257 | } |
258 | } |
259 | crate::ElementItems::Expressions(ty, reader) => { |
260 | validate_count(reader.count())?; |
261 | for expr in reader { |
262 | self.check_const_expr(&expr?, ValType::Ref(ty), features, types)?; |
263 | } |
264 | } |
265 | } |
266 | self.module.assert_mut().element_types.push(element_ty); |
267 | Ok(()) |
268 | } |
269 | |
270 | fn check_const_expr( |
271 | &mut self, |
272 | expr: &ConstExpr<'_>, |
273 | expected_ty: ValType, |
274 | features: &WasmFeatures, |
275 | types: &TypeList, |
276 | ) -> Result<()> { |
277 | let mut validator = VisitConstOperator { |
278 | offset: 0, |
279 | order: self.order, |
280 | uninserted_funcref: false, |
281 | ops: OperatorValidator::new_const_expr( |
282 | features, |
283 | expected_ty, |
284 | mem::take(&mut self.const_expr_allocs), |
285 | ), |
286 | resources: OperatorValidatorResources { |
287 | types, |
288 | module: &mut self.module, |
289 | }, |
290 | features, |
291 | }; |
292 | |
293 | let mut ops = expr.get_operators_reader(); |
294 | while !ops.eof() { |
295 | validator.offset = ops.original_position(); |
296 | ops.visit_operator(&mut validator)??; |
297 | } |
298 | validator.ops.finish(ops.original_position())?; |
299 | |
300 | // See comment in `RefFunc` below for why this is an assert. |
301 | assert!(!validator.uninserted_funcref); |
302 | |
303 | self.const_expr_allocs = validator.ops.into_allocations(); |
304 | |
305 | return Ok(()); |
306 | |
307 | struct VisitConstOperator<'a> { |
308 | offset: usize, |
309 | uninserted_funcref: bool, |
310 | ops: OperatorValidator, |
311 | resources: OperatorValidatorResources<'a>, |
312 | order: Order, |
313 | features: &'a WasmFeatures, |
314 | } |
315 | |
316 | impl VisitConstOperator<'_> { |
317 | fn validator(&mut self) -> impl VisitOperator<'_, Output = Result<()>> { |
318 | self.ops.with_resources(&self.resources, self.offset) |
319 | } |
320 | |
321 | fn validate_extended_const(&mut self, op: &str) -> Result<()> { |
322 | if self.ops.features.extended_const() { |
323 | Ok(()) |
324 | } else { |
325 | Err(BinaryReaderError::new( |
326 | format!( |
327 | "constant expression required: non-constant operator: {}" , |
328 | op |
329 | ), |
330 | self.offset, |
331 | )) |
332 | } |
333 | } |
334 | |
335 | fn validate_gc(&mut self, op: &str) -> Result<()> { |
336 | if self.features.gc() { |
337 | Ok(()) |
338 | } else { |
339 | Err(BinaryReaderError::new( |
340 | format!( |
341 | "constant expression required: non-constant operator: {}" , |
342 | op |
343 | ), |
344 | self.offset, |
345 | )) |
346 | } |
347 | } |
348 | |
349 | fn validate_shared_everything_threads(&mut self, op: &str) -> Result<()> { |
350 | if self.features.shared_everything_threads() { |
351 | Ok(()) |
352 | } else { |
353 | Err(BinaryReaderError::new( |
354 | format!( |
355 | "constant expression required: non-constant operator: {}" , |
356 | op |
357 | ), |
358 | self.offset, |
359 | )) |
360 | } |
361 | } |
362 | |
363 | fn validate_global(&mut self, index: u32) -> Result<()> { |
364 | let module = &self.resources.module; |
365 | let global = module.global_at(index, self.offset)?; |
366 | |
367 | if index >= module.num_imported_globals && !self.features.gc() { |
368 | return Err(BinaryReaderError::new( |
369 | "constant expression required: global.get of locally defined global" , |
370 | self.offset, |
371 | )); |
372 | } |
373 | if global.mutable { |
374 | return Err(BinaryReaderError::new( |
375 | "constant expression required: global.get of mutable global" , |
376 | self.offset, |
377 | )); |
378 | } |
379 | Ok(()) |
380 | } |
381 | |
382 | // Functions in initialization expressions are only valid in |
383 | // element segment initialization expressions and globals. In |
384 | // these contexts we want to record all function references. |
385 | // |
386 | // Initialization expressions can also be found in the data |
387 | // section, however. A `RefFunc` instruction in those situations |
388 | // is always invalid and needs to produce a validation error. In |
389 | // this situation, though, we can no longer modify |
390 | // the state since it's been "snapshot" already for |
391 | // parallel validation of functions. |
392 | // |
393 | // If we cannot modify the function references then this function |
394 | // *should* result in a validation error, but we defer that |
395 | // validation error to happen later. The `uninserted_funcref` |
396 | // boolean here is used to track this and will cause a panic |
397 | // (aka a fuzz bug) if we somehow forget to emit an error somewhere |
398 | // else. |
399 | fn insert_ref_func(&mut self, index: u32) { |
400 | if self.order == Order::Data { |
401 | self.uninserted_funcref = true; |
402 | } else { |
403 | self.resources |
404 | .module |
405 | .assert_mut() |
406 | .function_references |
407 | .insert(index); |
408 | } |
409 | } |
410 | |
411 | fn not_const(&self, instr: &str) -> BinaryReaderError { |
412 | BinaryReaderError::new( |
413 | format!("constant expression required: non-constant operator: {instr}" ), |
414 | self.offset, |
415 | ) |
416 | } |
417 | } |
418 | |
419 | macro_rules! define_visit_operator { |
420 | ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => { |
421 | $( |
422 | #[allow(unused_variables)] |
423 | fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { |
424 | define_visit_operator!(@visit self $visit $($($arg)*)?) |
425 | } |
426 | )* |
427 | }; |
428 | |
429 | // These are always valid in const expressions |
430 | (@visit $self:ident visit_i32_const $val:ident) => {{ |
431 | $self.validator().visit_i32_const($val) |
432 | }}; |
433 | (@visit $self:ident visit_i64_const $val:ident) => {{ |
434 | $self.validator().visit_i64_const($val) |
435 | }}; |
436 | (@visit $self:ident visit_f32_const $val:ident) => {{ |
437 | $self.validator().visit_f32_const($val) |
438 | }}; |
439 | (@visit $self:ident visit_f64_const $val:ident) => {{ |
440 | $self.validator().visit_f64_const($val) |
441 | }}; |
442 | (@visit $self:ident visit_v128_const $val:ident) => {{ |
443 | $self.validator().simd_visitor().unwrap().visit_v128_const($val) |
444 | }}; |
445 | (@visit $self:ident visit_ref_null $val:ident) => {{ |
446 | $self.validator().visit_ref_null($val) |
447 | }}; |
448 | (@visit $self:ident visit_end) => {{ |
449 | $self.validator().visit_end() |
450 | }}; |
451 | |
452 | |
453 | // These are valid const expressions when the extended-const proposal is enabled. |
454 | (@visit $self:ident visit_i32_add) => {{ |
455 | $self.validate_extended_const("i32.add" )?; |
456 | $self.validator().visit_i32_add() |
457 | }}; |
458 | (@visit $self:ident visit_i32_sub) => {{ |
459 | $self.validate_extended_const("i32.sub" )?; |
460 | $self.validator().visit_i32_sub() |
461 | }}; |
462 | (@visit $self:ident visit_i32_mul) => {{ |
463 | $self.validate_extended_const("i32.mul" )?; |
464 | $self.validator().visit_i32_mul() |
465 | }}; |
466 | (@visit $self:ident visit_i64_add) => {{ |
467 | $self.validate_extended_const("i64.add" )?; |
468 | $self.validator().visit_i64_add() |
469 | }}; |
470 | (@visit $self:ident visit_i64_sub) => {{ |
471 | $self.validate_extended_const("i64.sub" )?; |
472 | $self.validator().visit_i64_sub() |
473 | }}; |
474 | (@visit $self:ident visit_i64_mul) => {{ |
475 | $self.validate_extended_const("i64.mul" )?; |
476 | $self.validator().visit_i64_mul() |
477 | }}; |
478 | |
479 | // These are valid const expressions with the gc proposal is |
480 | // enabled. |
481 | (@visit $self:ident visit_struct_new $type_index:ident) => {{ |
482 | $self.validate_gc("struct.new" )?; |
483 | $self.validator().visit_struct_new($type_index) |
484 | }}; |
485 | (@visit $self:ident visit_struct_new_default $type_index:ident) => {{ |
486 | $self.validate_gc("struct.new_default" )?; |
487 | $self.validator().visit_struct_new_default($type_index) |
488 | }}; |
489 | (@visit $self:ident visit_array_new $type_index:ident) => {{ |
490 | $self.validate_gc("array.new" )?; |
491 | $self.validator().visit_array_new($type_index) |
492 | }}; |
493 | (@visit $self:ident visit_array_new_default $type_index:ident) => {{ |
494 | $self.validate_gc("array.new_default" )?; |
495 | $self.validator().visit_array_new_default($type_index) |
496 | }}; |
497 | (@visit $self:ident visit_array_new_fixed $type_index:ident $n:ident) => {{ |
498 | $self.validate_gc("array.new_fixed" )?; |
499 | $self.validator().visit_array_new_fixed($type_index, $n) |
500 | }}; |
501 | (@visit $self:ident visit_ref_i31) => {{ |
502 | $self.validate_gc("ref.i31" )?; |
503 | $self.validator().visit_ref_i31() |
504 | }}; |
505 | (@visit $self:ident visit_ref_i31_shared) => {{ |
506 | $self.validate_shared_everything_threads("ref.i31_shared" )?; |
507 | $self.validator().visit_ref_i31_shared() |
508 | }}; |
509 | |
510 | // `global.get` is a valid const expression for imported, immutable |
511 | // globals. |
512 | (@visit $self:ident visit_global_get $idx:ident) => {{ |
513 | $self.validate_global($idx)?; |
514 | $self.validator().visit_global_get($idx) |
515 | }}; |
516 | // `ref.func`, if it's in a `global` initializer, will insert into |
517 | // the set of referenced functions so it's processed here. |
518 | (@visit $self:ident visit_ref_func $idx:ident) => {{ |
519 | $self.insert_ref_func($idx); |
520 | $self.validator().visit_ref_func($idx) |
521 | }}; |
522 | |
523 | (@visit $self:ident $op:ident $($args:tt)*) => {{ |
524 | Err($self.not_const(stringify!($op))) |
525 | }} |
526 | } |
527 | |
528 | impl<'a> VisitOperator<'a> for VisitConstOperator<'a> { |
529 | type Output = Result<()>; |
530 | |
531 | #[cfg (feature = "simd" )] |
532 | fn simd_visitor( |
533 | &mut self, |
534 | ) -> Option<&mut dyn crate::VisitSimdOperator<'a, Output = Self::Output>> { |
535 | Some(self) |
536 | } |
537 | |
538 | crate::for_each_visit_operator!(define_visit_operator); |
539 | } |
540 | |
541 | #[cfg (feature = "simd" )] |
542 | impl<'a> VisitSimdOperator<'a> for VisitConstOperator<'a> { |
543 | crate::for_each_visit_simd_operator!(define_visit_operator); |
544 | } |
545 | } |
546 | } |
547 | |
548 | #[derive (Debug)] |
549 | pub(crate) struct Module { |
550 | // This is set once the code section starts. |
551 | // `WasmModuleResources` implementations use the snapshot to |
552 | // enable parallel validation of functions. |
553 | pub snapshot: Option<Arc<TypeList>>, |
554 | // Stores indexes into the validator's types list. |
555 | pub types: Vec<CoreTypeId>, |
556 | pub tables: Vec<TableType>, |
557 | pub memories: Vec<MemoryType>, |
558 | pub globals: Vec<GlobalType>, |
559 | pub element_types: Vec<RefType>, |
560 | pub data_count: Option<u32>, |
561 | // Stores indexes into `types`. |
562 | pub functions: Vec<u32>, |
563 | pub tags: Vec<CoreTypeId>, |
564 | pub function_references: Set<u32>, |
565 | pub imports: IndexMap<(String, String), Vec<EntityType>>, |
566 | pub exports: IndexMap<String, EntityType>, |
567 | pub type_size: u32, |
568 | num_imported_globals: u32, |
569 | num_imported_functions: u32, |
570 | } |
571 | |
572 | impl Module { |
573 | pub(crate) fn add_types( |
574 | &mut self, |
575 | rec_group: RecGroup, |
576 | features: &WasmFeatures, |
577 | types: &mut TypeAlloc, |
578 | offset: usize, |
579 | check_limit: bool, |
580 | ) -> Result<()> { |
581 | if check_limit { |
582 | check_max( |
583 | self.types.len(), |
584 | rec_group.types().len() as u32, |
585 | MAX_WASM_TYPES, |
586 | "types" , |
587 | offset, |
588 | )?; |
589 | } |
590 | self.canonicalize_and_intern_rec_group(features, types, rec_group, offset) |
591 | } |
592 | |
593 | pub fn add_import( |
594 | &mut self, |
595 | mut import: crate::Import, |
596 | features: &WasmFeatures, |
597 | types: &TypeList, |
598 | offset: usize, |
599 | ) -> Result<()> { |
600 | let entity = self.check_type_ref(&mut import.ty, features, types, offset)?; |
601 | |
602 | let (len, max, desc) = match import.ty { |
603 | TypeRef::Func(type_index) => { |
604 | self.functions.push(type_index); |
605 | self.num_imported_functions += 1; |
606 | (self.functions.len(), MAX_WASM_FUNCTIONS, "functions" ) |
607 | } |
608 | TypeRef::Table(ty) => { |
609 | self.tables.push(ty); |
610 | (self.tables.len(), self.max_tables(features), "tables" ) |
611 | } |
612 | TypeRef::Memory(ty) => { |
613 | self.memories.push(ty); |
614 | (self.memories.len(), self.max_memories(features), "memories" ) |
615 | } |
616 | TypeRef::Tag(ty) => { |
617 | self.tags.push(self.types[ty.func_type_idx as usize]); |
618 | (self.tags.len(), MAX_WASM_TAGS, "tags" ) |
619 | } |
620 | TypeRef::Global(ty) => { |
621 | if !features.mutable_global() && ty.mutable { |
622 | return Err(BinaryReaderError::new( |
623 | "mutable global support is not enabled" , |
624 | offset, |
625 | )); |
626 | } |
627 | self.globals.push(ty); |
628 | self.num_imported_globals += 1; |
629 | (self.globals.len(), MAX_WASM_GLOBALS, "globals" ) |
630 | } |
631 | }; |
632 | |
633 | check_max(len, 0, max, desc, offset)?; |
634 | |
635 | self.type_size = combine_type_sizes(self.type_size, entity.info(types).size(), offset)?; |
636 | |
637 | self.imports |
638 | .entry((import.module.to_string(), import.name.to_string())) |
639 | .or_default() |
640 | .push(entity); |
641 | |
642 | Ok(()) |
643 | } |
644 | |
645 | pub fn add_export( |
646 | &mut self, |
647 | name: &str, |
648 | ty: EntityType, |
649 | features: &WasmFeatures, |
650 | offset: usize, |
651 | check_limit: bool, |
652 | types: &TypeList, |
653 | ) -> Result<()> { |
654 | if !features.mutable_global() { |
655 | if let EntityType::Global(global_type) = ty { |
656 | if global_type.mutable { |
657 | return Err(BinaryReaderError::new( |
658 | "mutable global support is not enabled" , |
659 | offset, |
660 | )); |
661 | } |
662 | } |
663 | } |
664 | |
665 | if check_limit { |
666 | check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports" , offset)?; |
667 | } |
668 | |
669 | self.type_size = combine_type_sizes(self.type_size, ty.info(types).size(), offset)?; |
670 | |
671 | match self.exports.insert(name.to_string(), ty) { |
672 | Some(_) => Err(format_err!( |
673 | offset, |
674 | "duplicate export name ` {name}` already defined" |
675 | )), |
676 | None => Ok(()), |
677 | } |
678 | } |
679 | |
680 | pub fn add_function(&mut self, type_index: u32, types: &TypeList, offset: usize) -> Result<()> { |
681 | self.func_type_at(type_index, types, offset)?; |
682 | self.functions.push(type_index); |
683 | Ok(()) |
684 | } |
685 | |
686 | pub fn add_memory( |
687 | &mut self, |
688 | ty: MemoryType, |
689 | features: &WasmFeatures, |
690 | offset: usize, |
691 | ) -> Result<()> { |
692 | self.check_memory_type(&ty, features, offset)?; |
693 | self.memories.push(ty); |
694 | Ok(()) |
695 | } |
696 | |
697 | pub fn add_tag( |
698 | &mut self, |
699 | ty: TagType, |
700 | features: &WasmFeatures, |
701 | types: &TypeList, |
702 | offset: usize, |
703 | ) -> Result<()> { |
704 | self.check_tag_type(&ty, features, types, offset)?; |
705 | self.tags.push(self.types[ty.func_type_idx as usize]); |
706 | Ok(()) |
707 | } |
708 | |
709 | fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> { |
710 | let id = self.type_id_at(idx, offset)?; |
711 | Ok(&types[id]) |
712 | } |
713 | |
714 | fn func_type_at<'a>( |
715 | &self, |
716 | type_index: u32, |
717 | types: &'a TypeList, |
718 | offset: usize, |
719 | ) -> Result<&'a FuncType> { |
720 | match &self |
721 | .sub_type_at(types, type_index, offset)? |
722 | .composite_type |
723 | .inner |
724 | { |
725 | CompositeInnerType::Func(f) => Ok(f), |
726 | _ => bail!(offset, "type index {type_index} is not a function type" ), |
727 | } |
728 | } |
729 | |
730 | pub fn check_type_ref( |
731 | &self, |
732 | type_ref: &mut TypeRef, |
733 | features: &WasmFeatures, |
734 | types: &TypeList, |
735 | offset: usize, |
736 | ) -> Result<EntityType> { |
737 | Ok(match type_ref { |
738 | TypeRef::Func(type_index) => { |
739 | self.func_type_at(*type_index, types, offset)?; |
740 | EntityType::Func(self.types[*type_index as usize]) |
741 | } |
742 | TypeRef::Table(t) => { |
743 | self.check_table_type(t, features, types, offset)?; |
744 | EntityType::Table(*t) |
745 | } |
746 | TypeRef::Memory(t) => { |
747 | self.check_memory_type(t, features, offset)?; |
748 | EntityType::Memory(*t) |
749 | } |
750 | TypeRef::Tag(t) => { |
751 | self.check_tag_type(t, features, types, offset)?; |
752 | EntityType::Tag(self.types[t.func_type_idx as usize]) |
753 | } |
754 | TypeRef::Global(t) => { |
755 | self.check_global_type(t, features, types, offset)?; |
756 | EntityType::Global(*t) |
757 | } |
758 | }) |
759 | } |
760 | |
761 | fn check_table_type( |
762 | &self, |
763 | ty: &mut TableType, |
764 | features: &WasmFeatures, |
765 | types: &TypeList, |
766 | offset: usize, |
767 | ) -> Result<()> { |
768 | // The `funcref` value type is allowed all the way back to the MVP, so |
769 | // don't check it here. |
770 | if ty.element_type != RefType::FUNCREF { |
771 | self.check_ref_type(&mut ty.element_type, features, offset)? |
772 | } |
773 | |
774 | if ty.table64 && !features.memory64() { |
775 | return Err(BinaryReaderError::new( |
776 | "memory64 must be enabled for 64-bit tables" , |
777 | offset, |
778 | )); |
779 | } |
780 | |
781 | self.check_limits(ty.initial, ty.maximum, offset)?; |
782 | |
783 | if ty.shared { |
784 | if !features.shared_everything_threads() { |
785 | return Err(BinaryReaderError::new( |
786 | "shared tables require the shared-everything-threads proposal" , |
787 | offset, |
788 | )); |
789 | } |
790 | |
791 | if !types.reftype_is_shared(ty.element_type) { |
792 | return Err(BinaryReaderError::new( |
793 | "shared tables must have a shared element type" , |
794 | offset, |
795 | )); |
796 | } |
797 | } |
798 | |
799 | Ok(()) |
800 | } |
801 | |
802 | fn check_memory_type( |
803 | &self, |
804 | ty: &MemoryType, |
805 | features: &WasmFeatures, |
806 | offset: usize, |
807 | ) -> Result<()> { |
808 | self.check_limits(ty.initial, ty.maximum, offset)?; |
809 | let (page_size, page_size_log2) = if let Some(page_size_log2) = ty.page_size_log2 { |
810 | if !features.custom_page_sizes() { |
811 | return Err(BinaryReaderError::new( |
812 | "the custom page sizes proposal must be enabled to \ |
813 | customize a memory's page size" , |
814 | offset, |
815 | )); |
816 | } |
817 | // Currently 2**0 and 2**16 are the only valid page sizes, but this |
818 | // may be relaxed to allow any power of two in the future. |
819 | if page_size_log2 != 0 && page_size_log2 != 16 { |
820 | return Err(BinaryReaderError::new("invalid custom page size" , offset)); |
821 | } |
822 | let page_size = 1_u64 << page_size_log2; |
823 | debug_assert!(page_size.is_power_of_two()); |
824 | debug_assert!(page_size == DEFAULT_WASM_PAGE_SIZE || page_size == 1); |
825 | (page_size, page_size_log2) |
826 | } else { |
827 | let page_size_log2 = 16; |
828 | debug_assert_eq!(DEFAULT_WASM_PAGE_SIZE, 1 << page_size_log2); |
829 | (DEFAULT_WASM_PAGE_SIZE, page_size_log2) |
830 | }; |
831 | let (true_maximum, err) = if ty.memory64 { |
832 | if !features.memory64() { |
833 | return Err(BinaryReaderError::new( |
834 | "memory64 must be enabled for 64-bit memories" , |
835 | offset, |
836 | )); |
837 | } |
838 | ( |
839 | max_wasm_memory64_pages(page_size), |
840 | format!( |
841 | "memory size must be at most 2** {} pages" , |
842 | 64 - page_size_log2 |
843 | ), |
844 | ) |
845 | } else { |
846 | let max = max_wasm_memory32_pages(page_size); |
847 | ( |
848 | max, |
849 | format!("memory size must be at most {max} pages (4GiB)" ), |
850 | ) |
851 | }; |
852 | if ty.initial > true_maximum { |
853 | return Err(BinaryReaderError::new(err, offset)); |
854 | } |
855 | if let Some(maximum) = ty.maximum { |
856 | if maximum > true_maximum { |
857 | return Err(BinaryReaderError::new(err, offset)); |
858 | } |
859 | } |
860 | if ty.shared { |
861 | if !features.threads() { |
862 | return Err(BinaryReaderError::new( |
863 | "threads must be enabled for shared memories" , |
864 | offset, |
865 | )); |
866 | } |
867 | if ty.maximum.is_none() { |
868 | return Err(BinaryReaderError::new( |
869 | "shared memory must have maximum size" , |
870 | offset, |
871 | )); |
872 | } |
873 | } |
874 | Ok(()) |
875 | } |
876 | |
877 | #[cfg (feature = "component-model" )] |
878 | pub(crate) fn imports_for_module_type( |
879 | &self, |
880 | offset: usize, |
881 | ) -> Result<IndexMap<(String, String), EntityType>> { |
882 | // Ensure imports are unique, which is a requirement of the component model |
883 | self.imports |
884 | .iter() |
885 | .map(|((module, name), types)| { |
886 | if types.len() != 1 { |
887 | bail!( |
888 | offset, |
889 | "module has a duplicate import name ` {module}: {name}` \ |
890 | that is not allowed in components" , |
891 | ); |
892 | } |
893 | Ok(((module.clone(), name.clone()), types[0])) |
894 | }) |
895 | .collect::<Result<_>>() |
896 | } |
897 | |
898 | fn check_value_type( |
899 | &self, |
900 | ty: &mut ValType, |
901 | features: &WasmFeatures, |
902 | offset: usize, |
903 | ) -> Result<()> { |
904 | // The above only checks the value type for features. |
905 | // We must check it if it's a reference. |
906 | match ty { |
907 | ValType::Ref(rt) => self.check_ref_type(rt, features, offset), |
908 | _ => features |
909 | .check_value_type(*ty) |
910 | .map_err(|e| BinaryReaderError::new(e, offset)), |
911 | } |
912 | } |
913 | |
914 | fn check_ref_type( |
915 | &self, |
916 | ty: &mut RefType, |
917 | features: &WasmFeatures, |
918 | offset: usize, |
919 | ) -> Result<()> { |
920 | features |
921 | .check_ref_type(*ty) |
922 | .map_err(|e| BinaryReaderError::new(e, offset))?; |
923 | let mut hty = ty.heap_type(); |
924 | self.check_heap_type(&mut hty, offset)?; |
925 | *ty = RefType::new(ty.is_nullable(), hty).unwrap(); |
926 | Ok(()) |
927 | } |
928 | |
929 | fn check_heap_type(&self, ty: &mut HeapType, offset: usize) -> Result<()> { |
930 | // Check that the heap type is valid. |
931 | let type_index = match ty { |
932 | HeapType::Abstract { .. } => return Ok(()), |
933 | HeapType::Concrete(type_index) => type_index, |
934 | }; |
935 | match type_index { |
936 | UnpackedIndex::Module(idx) => { |
937 | let id = self.type_id_at(*idx, offset)?; |
938 | *type_index = UnpackedIndex::Id(id); |
939 | Ok(()) |
940 | } |
941 | // Types at this stage should not be canonicalized. All |
942 | // canonicalized types should already be validated meaning they |
943 | // shouldn't be double-checked here again. |
944 | UnpackedIndex::RecGroup(_) | UnpackedIndex::Id(_) => unreachable!(), |
945 | } |
946 | } |
947 | |
948 | fn check_tag_type( |
949 | &self, |
950 | ty: &TagType, |
951 | features: &WasmFeatures, |
952 | types: &TypeList, |
953 | offset: usize, |
954 | ) -> Result<()> { |
955 | if !features.exceptions() { |
956 | return Err(BinaryReaderError::new( |
957 | "exceptions proposal not enabled" , |
958 | offset, |
959 | )); |
960 | } |
961 | let ty = self.func_type_at(ty.func_type_idx, types, offset)?; |
962 | if !ty.results().is_empty() && !features.stack_switching() { |
963 | return Err(BinaryReaderError::new( |
964 | "invalid exception type: non-empty tag result type" , |
965 | offset, |
966 | )); |
967 | } |
968 | Ok(()) |
969 | } |
970 | |
971 | fn check_global_type( |
972 | &self, |
973 | ty: &mut GlobalType, |
974 | features: &WasmFeatures, |
975 | types: &TypeList, |
976 | offset: usize, |
977 | ) -> Result<()> { |
978 | self.check_value_type(&mut ty.content_type, features, offset)?; |
979 | if ty.shared { |
980 | if !features.shared_everything_threads() { |
981 | return Err(BinaryReaderError::new( |
982 | "shared globals require the shared-everything-threads proposal" , |
983 | offset, |
984 | )); |
985 | } |
986 | if !types.valtype_is_shared(ty.content_type) { |
987 | return Err(BinaryReaderError::new( |
988 | "shared globals must have a shared value type" , |
989 | offset, |
990 | )); |
991 | } |
992 | } |
993 | Ok(()) |
994 | } |
995 | |
996 | fn check_limits<T>(&self, initial: T, maximum: Option<T>, offset: usize) -> Result<()> |
997 | where |
998 | T: Into<u64>, |
999 | { |
1000 | if let Some(max) = maximum { |
1001 | if initial.into() > max.into() { |
1002 | return Err(BinaryReaderError::new( |
1003 | "size minimum must not be greater than maximum" , |
1004 | offset, |
1005 | )); |
1006 | } |
1007 | } |
1008 | Ok(()) |
1009 | } |
1010 | |
1011 | pub fn max_tables(&self, features: &WasmFeatures) -> usize { |
1012 | if features.reference_types() { |
1013 | MAX_WASM_TABLES |
1014 | } else { |
1015 | 1 |
1016 | } |
1017 | } |
1018 | |
1019 | pub fn max_memories(&self, features: &WasmFeatures) -> usize { |
1020 | if features.multi_memory() { |
1021 | MAX_WASM_MEMORIES |
1022 | } else { |
1023 | 1 |
1024 | } |
1025 | } |
1026 | |
1027 | pub fn export_to_entity_type( |
1028 | &mut self, |
1029 | export: &crate::Export, |
1030 | offset: usize, |
1031 | ) -> Result<EntityType> { |
1032 | let check = |ty: &str, index: u32, total: usize| { |
1033 | if index as usize >= total { |
1034 | Err(format_err!( |
1035 | offset, |
1036 | "unknown {ty} {index}: exported {ty} index out of bounds" , |
1037 | )) |
1038 | } else { |
1039 | Ok(()) |
1040 | } |
1041 | }; |
1042 | |
1043 | Ok(match export.kind { |
1044 | ExternalKind::Func => { |
1045 | check("function" , export.index, self.functions.len())?; |
1046 | self.function_references.insert(export.index); |
1047 | EntityType::Func(self.types[self.functions[export.index as usize] as usize]) |
1048 | } |
1049 | ExternalKind::Table => { |
1050 | check("table" , export.index, self.tables.len())?; |
1051 | EntityType::Table(self.tables[export.index as usize]) |
1052 | } |
1053 | ExternalKind::Memory => { |
1054 | check("memory" , export.index, self.memories.len())?; |
1055 | EntityType::Memory(self.memories[export.index as usize]) |
1056 | } |
1057 | ExternalKind::Global => { |
1058 | check("global" , export.index, self.globals.len())?; |
1059 | EntityType::Global(self.globals[export.index as usize]) |
1060 | } |
1061 | ExternalKind::Tag => { |
1062 | check("tag" , export.index, self.tags.len())?; |
1063 | EntityType::Tag(self.tags[export.index as usize]) |
1064 | } |
1065 | }) |
1066 | } |
1067 | |
1068 | pub fn get_func_type<'a>( |
1069 | &self, |
1070 | func_idx: u32, |
1071 | types: &'a TypeList, |
1072 | offset: usize, |
1073 | ) -> Result<&'a FuncType> { |
1074 | match self.functions.get(func_idx as usize) { |
1075 | Some(idx) => self.func_type_at(*idx, types, offset), |
1076 | None => Err(format_err!( |
1077 | offset, |
1078 | "unknown function {func_idx}: func index out of bounds" , |
1079 | )), |
1080 | } |
1081 | } |
1082 | |
1083 | fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> { |
1084 | match self.globals.get(idx as usize) { |
1085 | Some(t) => Ok(t), |
1086 | None => Err(format_err!( |
1087 | offset, |
1088 | "unknown global {idx}: global index out of bounds" |
1089 | )), |
1090 | } |
1091 | } |
1092 | |
1093 | fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> { |
1094 | match self.tables.get(idx as usize) { |
1095 | Some(t) => Ok(t), |
1096 | None => Err(format_err!( |
1097 | offset, |
1098 | "unknown table {idx}: table index out of bounds" |
1099 | )), |
1100 | } |
1101 | } |
1102 | |
1103 | fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> { |
1104 | match self.memories.get(idx as usize) { |
1105 | Some(t) => Ok(t), |
1106 | None => Err(format_err!( |
1107 | offset, |
1108 | "unknown memory {idx}: memory index out of bounds" |
1109 | )), |
1110 | } |
1111 | } |
1112 | } |
1113 | |
1114 | impl InternRecGroup for Module { |
1115 | fn add_type_id(&mut self, id: CoreTypeId) { |
1116 | self.types.push(id); |
1117 | } |
1118 | |
1119 | fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> { |
1120 | self.types |
1121 | .get(idx as usize) |
1122 | .copied() |
1123 | .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds" )) |
1124 | } |
1125 | |
1126 | fn types_len(&self) -> u32 { |
1127 | u32::try_from(self.types.len()).unwrap() |
1128 | } |
1129 | } |
1130 | |
1131 | impl Default for Module { |
1132 | fn default() -> Self { |
1133 | Self { |
1134 | snapshot: Default::default(), |
1135 | types: Default::default(), |
1136 | tables: Default::default(), |
1137 | memories: Default::default(), |
1138 | globals: Default::default(), |
1139 | element_types: Default::default(), |
1140 | data_count: Default::default(), |
1141 | functions: Default::default(), |
1142 | tags: Default::default(), |
1143 | function_references: Default::default(), |
1144 | imports: Default::default(), |
1145 | exports: Default::default(), |
1146 | type_size: 1, |
1147 | num_imported_globals: Default::default(), |
1148 | num_imported_functions: Default::default(), |
1149 | } |
1150 | } |
1151 | } |
1152 | |
1153 | struct OperatorValidatorResources<'a> { |
1154 | module: &'a mut MaybeOwned<Module>, |
1155 | types: &'a TypeList, |
1156 | } |
1157 | |
1158 | impl WasmModuleResources for OperatorValidatorResources<'_> { |
1159 | fn table_at(&self, at: u32) -> Option<TableType> { |
1160 | self.module.tables.get(at as usize).cloned() |
1161 | } |
1162 | |
1163 | fn memory_at(&self, at: u32) -> Option<MemoryType> { |
1164 | self.module.memories.get(at as usize).cloned() |
1165 | } |
1166 | |
1167 | fn tag_at(&self, at: u32) -> Option<&FuncType> { |
1168 | let type_id = *self.module.tags.get(at as usize)?; |
1169 | Some(self.types[type_id].unwrap_func()) |
1170 | } |
1171 | |
1172 | fn global_at(&self, at: u32) -> Option<GlobalType> { |
1173 | self.module.globals.get(at as usize).cloned() |
1174 | } |
1175 | |
1176 | fn sub_type_at(&self, at: u32) -> Option<&SubType> { |
1177 | let id = *self.module.types.get(at as usize)?; |
1178 | Some(&self.types[id]) |
1179 | } |
1180 | |
1181 | fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType { |
1182 | &self.types[at] |
1183 | } |
1184 | |
1185 | fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> { |
1186 | let type_index = self.module.functions.get(at as usize)?; |
1187 | self.module.types.get(*type_index as usize).copied() |
1188 | } |
1189 | |
1190 | fn type_index_of_function(&self, at: u32) -> Option<u32> { |
1191 | self.module.functions.get(at as usize).copied() |
1192 | } |
1193 | |
1194 | fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> { |
1195 | self.module.check_heap_type(t, offset) |
1196 | } |
1197 | |
1198 | fn top_type(&self, heap_type: &HeapType) -> HeapType { |
1199 | self.types.top_type(heap_type) |
1200 | } |
1201 | |
1202 | fn element_type_at(&self, at: u32) -> Option<RefType> { |
1203 | self.module.element_types.get(at as usize).cloned() |
1204 | } |
1205 | |
1206 | fn is_subtype(&self, a: ValType, b: ValType) -> bool { |
1207 | self.types.valtype_is_subtype(a, b) |
1208 | } |
1209 | |
1210 | fn is_shared(&self, ty: RefType) -> bool { |
1211 | self.types.reftype_is_shared(ty) |
1212 | } |
1213 | |
1214 | fn element_count(&self) -> u32 { |
1215 | self.module.element_types.len() as u32 |
1216 | } |
1217 | |
1218 | fn data_count(&self) -> Option<u32> { |
1219 | self.module.data_count |
1220 | } |
1221 | |
1222 | fn is_function_referenced(&self, idx: u32) -> bool { |
1223 | self.module.function_references.contains(&idx) |
1224 | } |
1225 | } |
1226 | |
1227 | /// The implementation of [`WasmModuleResources`] used by |
1228 | /// [`Validator`](crate::Validator). |
1229 | #[derive (Debug)] |
1230 | pub struct ValidatorResources(pub(crate) Arc<Module>); |
1231 | |
1232 | impl WasmModuleResources for ValidatorResources { |
1233 | fn table_at(&self, at: u32) -> Option<TableType> { |
1234 | self.0.tables.get(at as usize).cloned() |
1235 | } |
1236 | |
1237 | fn memory_at(&self, at: u32) -> Option<MemoryType> { |
1238 | self.0.memories.get(at as usize).cloned() |
1239 | } |
1240 | |
1241 | fn tag_at(&self, at: u32) -> Option<&FuncType> { |
1242 | let id = *self.0.tags.get(at as usize)?; |
1243 | let types = self.0.snapshot.as_ref().unwrap(); |
1244 | match &types[id].composite_type.inner { |
1245 | CompositeInnerType::Func(f) => Some(f), |
1246 | _ => None, |
1247 | } |
1248 | } |
1249 | |
1250 | fn global_at(&self, at: u32) -> Option<GlobalType> { |
1251 | self.0.globals.get(at as usize).cloned() |
1252 | } |
1253 | |
1254 | fn sub_type_at(&self, at: u32) -> Option<&SubType> { |
1255 | let id = *self.0.types.get(at as usize)?; |
1256 | let types = self.0.snapshot.as_ref().unwrap(); |
1257 | Some(&types[id]) |
1258 | } |
1259 | |
1260 | fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType { |
1261 | let types = self.0.snapshot.as_ref().unwrap(); |
1262 | &types[at] |
1263 | } |
1264 | |
1265 | fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> { |
1266 | let type_index = *self.0.functions.get(at as usize)?; |
1267 | self.0.types.get(type_index as usize).copied() |
1268 | } |
1269 | |
1270 | fn type_index_of_function(&self, at: u32) -> Option<u32> { |
1271 | self.0.functions.get(at as usize).copied() |
1272 | } |
1273 | |
1274 | fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> { |
1275 | self.0.check_heap_type(t, offset) |
1276 | } |
1277 | |
1278 | fn top_type(&self, heap_type: &HeapType) -> HeapType { |
1279 | self.0.snapshot.as_ref().unwrap().top_type(heap_type) |
1280 | } |
1281 | |
1282 | fn element_type_at(&self, at: u32) -> Option<RefType> { |
1283 | self.0.element_types.get(at as usize).cloned() |
1284 | } |
1285 | |
1286 | fn is_subtype(&self, a: ValType, b: ValType) -> bool { |
1287 | self.0.snapshot.as_ref().unwrap().valtype_is_subtype(a, b) |
1288 | } |
1289 | |
1290 | fn is_shared(&self, ty: RefType) -> bool { |
1291 | self.0.snapshot.as_ref().unwrap().reftype_is_shared(ty) |
1292 | } |
1293 | |
1294 | fn element_count(&self) -> u32 { |
1295 | self.0.element_types.len() as u32 |
1296 | } |
1297 | |
1298 | fn data_count(&self) -> Option<u32> { |
1299 | self.0.data_count |
1300 | } |
1301 | |
1302 | fn is_function_referenced(&self, idx: u32) -> bool { |
1303 | self.0.function_references.contains(&idx) |
1304 | } |
1305 | } |
1306 | |
1307 | const _: () = { |
1308 | fn assert_send<T: Send>() {} |
1309 | |
1310 | // Assert that `ValidatorResources` is Send so function validation |
1311 | // can be parallelizable |
1312 | fn assert() { |
1313 | assert_send::<ValidatorResources>(); |
1314 | } |
1315 | }; |
1316 | |
1317 | mod arc { |
1318 | use alloc::sync::Arc; |
1319 | use core::ops::Deref; |
1320 | |
1321 | enum Inner<T> { |
1322 | Owned(T), |
1323 | Shared(Arc<T>), |
1324 | |
1325 | Empty, // Only used for swapping from owned to shared. |
1326 | } |
1327 | |
1328 | pub struct MaybeOwned<T> { |
1329 | inner: Inner<T>, |
1330 | } |
1331 | |
1332 | impl<T> MaybeOwned<T> { |
1333 | #[inline ] |
1334 | fn as_mut(&mut self) -> Option<&mut T> { |
1335 | match &mut self.inner { |
1336 | Inner::Owned(x) => Some(x), |
1337 | Inner::Shared(_) => None, |
1338 | Inner::Empty => Self::unreachable(), |
1339 | } |
1340 | } |
1341 | |
1342 | #[inline ] |
1343 | pub fn assert_mut(&mut self) -> &mut T { |
1344 | self.as_mut().unwrap() |
1345 | } |
1346 | |
1347 | pub fn arc(&mut self) -> &Arc<T> { |
1348 | self.make_shared(); |
1349 | match &self.inner { |
1350 | Inner::Shared(x) => x, |
1351 | _ => Self::unreachable(), |
1352 | } |
1353 | } |
1354 | |
1355 | #[inline ] |
1356 | fn make_shared(&mut self) { |
1357 | if let Inner::Shared(_) = self.inner { |
1358 | return; |
1359 | } |
1360 | |
1361 | let inner = core::mem::replace(&mut self.inner, Inner::Empty); |
1362 | let x = match inner { |
1363 | Inner::Owned(x) => x, |
1364 | _ => Self::unreachable(), |
1365 | }; |
1366 | let x = Arc::new(x); |
1367 | self.inner = Inner::Shared(x); |
1368 | } |
1369 | |
1370 | #[cold ] |
1371 | #[inline (never)] |
1372 | fn unreachable() -> ! { |
1373 | unreachable!() |
1374 | } |
1375 | } |
1376 | |
1377 | impl<T: Default> Default for MaybeOwned<T> { |
1378 | fn default() -> MaybeOwned<T> { |
1379 | MaybeOwned { |
1380 | inner: Inner::Owned(T::default()), |
1381 | } |
1382 | } |
1383 | } |
1384 | |
1385 | impl<T> Deref for MaybeOwned<T> { |
1386 | type Target = T; |
1387 | |
1388 | fn deref(&self) -> &T { |
1389 | match &self.inner { |
1390 | Inner::Owned(x) => x, |
1391 | Inner::Shared(x) => x, |
1392 | Inner::Empty => Self::unreachable(), |
1393 | } |
1394 | } |
1395 | } |
1396 | } |
1397 | |