/* Copyright 2018 Mozilla Foundation * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
usecrate::prelude::*; usecrate::{
limits::*, AbstractHeapType, BinaryReaderError, Encoding, FromReader, FunctionBody, HeapType,
Parser, Payload, RefType, Result, SectionLimited, ValType, WasmFeatures, WASM_MODULE_VERSION,
}; use ::core::mem; use ::core::ops::Range; use ::core::sync::atomic::{AtomicUsize, Ordering}; use alloc::sync::Arc;
/// Test whether the given buffer contains a valid WebAssembly module or component, /// analogous to [`WebAssembly.validate`][js] in the JS API. /// /// This functions requires the bytes to validate are entirely resident in memory. /// Additionally this validates the given bytes with the default set of WebAssembly /// features implemented by `wasmparser`. /// /// For more fine-tuned control over validation it's recommended to review the /// documentation of [`Validator`]. /// /// Upon success, the type information for the top-level module or component will /// be returned. /// /// [js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/validate pubfn validate(bytes: &[u8]) -> Result<Types> {
Validator::new().validate_all(bytes)
}
fn check_max(cur_len: usize, amt_added: u32, max: usize, desc: &str, offset: usize) -> Result<()> { if max
.checked_sub(cur_len)
.and_then(|amt| amt.checked_sub(amt_added as usize))
.is_none()
{ if max == 1 {
bail!(offset, "multiple {desc}");
}
bail!(offset, "{desc} count exceeds limit of {max}");
}
Ok(())
}
fn combine_type_sizes(a: u32, b: u32, offset: usize) -> Result<u32> { match a.checked_add(b) {
Some(sum) if sum < MAX_WASM_TYPE_SIZE => Ok(sum),
_ => Err(format_err!(
offset, "effective type size exceeds the limit of {MAX_WASM_TYPE_SIZE}",
)),
}
}
/// A unique identifier for a particular `Validator`. /// /// Allows you to save the `ValidatorId` of the [`Validator`][crate::Validator] /// you get identifiers out of (e.g. [`CoreTypeId`][crate::types::CoreTypeId]) /// and then later assert that you are pairing those identifiers with the same /// `Validator` instance when accessing the identifier's associated data. #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] pubstruct ValidatorId(usize);
/// Validator for a WebAssembly binary module or component. /// /// This structure encapsulates state necessary to validate a WebAssembly /// binary. This implements validation as defined by the [core /// specification][core]. A `Validator` is designed, like /// [`Parser`], to accept incremental input over time. /// Additionally a `Validator` is also designed for parallel validation of /// functions as they are received. /// /// It's expected that you'll be using a [`Parser`] in tandem with a /// `Validator`. As each [`Payload`](crate::Payload) is received from a /// [`Parser`] you'll pass it into a `Validator` to test the validity of the /// payload. Note that all payloads received from a [`Parser`] are expected to /// be passed to a [`Validator`]. For example if you receive /// [`Payload::TypeSection`](crate::Payload) you'll call /// [`Validator::type_section`] to validate this. /// /// The design of [`Validator`] is intended that you'll interleave, in your own /// application's processing, calls to validation. Each variant, after it's /// received, will be validated and then your application would proceed as /// usual. At all times, however, you'll have access to the [`Validator`] and /// the validation context up to that point. This enables applications to check /// the types of functions and learn how many globals there are, for example. /// /// [core]: https://webassembly.github.io/spec/core/valid/index.html #[derive(Default)] pubstruct Validator {
id: ValidatorId,
/// The current state of the validator.
state: State,
/// The global type space used by the validator and any sub-validators.
types: TypeAlloc,
/// The module state when parsing a WebAssembly module.
module: Option<ModuleState>,
/// With the component model enabled, this stores the pushed component states. /// The top of the stack is the current component state. #[cfg(feature = "component-model")]
components: Vec<ComponentState>,
/// Enabled WebAssembly feature flags, dictating what's valid and what /// isn't.
features: WasmFeatures,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)] enum State { /// A header has not yet been parsed. /// /// The value is the expected encoding for the header.
Unparsed(Option<Encoding>), /// A module header has been parsed. /// /// The associated module state is available via [`Validator::module`].
Module, /// A component header has been parsed. /// /// The associated component state exists at the top of the /// validator's [`Validator::components`] stack. #[cfg(feature = "component-model")]
Component, /// The parse has completed and no more data is expected.
End,
}
impl State { fn ensure_parsable(&self, offset: usize) -> Result<()> { matchself { Self::Module => Ok(()), #[cfg(feature = "component-model")] Self::Component => Ok(()), Self::Unparsed(_) => Err(BinaryReaderError::new( "unexpected section before header was parsed",
offset,
)), Self::End => Err(BinaryReaderError::new( "unexpected section after parsing has completed",
offset,
)),
}
}
impl Default for State { fn default() -> Self { Self::Unparsed(None)
}
}
impl WasmFeatures { /// NOTE: This only checks that the value type corresponds to the feature set!! /// /// To check that reference types are valid, we need access to the module /// types. Use module.check_value_type. pub(crate) fn check_value_type(&self, ty: ValType) -> Result<(), &an style='color:blue'>'static str> { match ty {
ValType::I32 | ValType::I64 => Ok(()),
ValType::F32 | ValType::F64 => { ifself.floats() {
Ok(())
} else {
Err("floating-point support is disabled")
}
}
ValType::Ref(r) => self.check_ref_type(r),
ValType::V128 => { ifself.simd() {
Ok(())
} else {
Err("SIMD support is not enabled")
}
}
}
}
pub(crate) fn check_ref_type(&self, r: RefType) -> Result<(), &style='color:blue'>'static str> { if !self.reference_types() { return Err("reference types support is not enabled");
} match r.heap_type() {
HeapType::Concrete(_) => { // Note that `self.gc_types()` is not checked here because // concrete pointers to function types are allowed. GC types // are disallowed by instead rejecting the definition of // array/struct types and only allowing the definition of // function types.
// Indexed types require either the function-references or gc // proposal as gc implies function references here. ifself.function_references() || self.gc() {
Ok(())
} else {
Err("function references required for index reference types")
}
}
HeapType::Abstract { shared, ty } => { use AbstractHeapType::*; if shared && !self.shared_everything_threads() { return Err( "shared reference types require the shared-everything-threads proposal",
);
}
// Apply the "gc-types" feature which disallows all heap types // except exnref/funcref. if !self.gc_types() && ty != Func && ty != Exn { return Err("gc types are disallowed but found type which requires gc");
}
match (ty, r.is_nullable()) { // funcref/externref only require `reference-types`.
(Func, true) | (Extern, true) => Ok(()),
// These types were added in the gc proposal.
(Any | None | Eq | Struct | Array | I31 | NoExtern | NoFunc, _) => { ifself.gc() {
Ok(())
} else {
Err("heap types not supported without the gc feature")
}
}
// These types were added in the exception-handling proposal.
(Exn | NoExn, _) => { ifself.exceptions() {
Ok(())
} else {
Err("exception refs not supported without the exception handling feature")
}
}
// These types were added in the stack switching proposal.
(Cont | NoCont, _) => { ifself.stack_switching() {
Ok(())
} else {
Err("continuation refs not supported without the stack switching feature")
}
}
}
}
}
}
}
/// Possible return values from [`Validator::payload`]. #[allow(clippy::large_enum_variant)] pubenum ValidPayload<'a> { /// The payload validated, no further action need be taken.
Ok, /// The payload validated, but it started a nested module or component. /// /// This result indicates that the specified parser should be used instead /// of the currently-used parser until this returned one ends.
Parser(Parser), /// A function was found to be validate.
Func(FuncToValidate<ValidatorResources>, FunctionBody<'a>), /// The end payload was validated and the types known to the validator /// are provided.
End(Types),
}
impl Validator { /// Creates a new [`Validator`] ready to validate a WebAssembly module /// or component. /// /// The new validator will receive payloads parsed from /// [`Parser`], and expects the first payload received to be /// the version header from the parser. pubfn new() -> Validator {
Validator::default()
}
/// Creates a new [`Validator`] which has the specified set of wasm /// features activated for validation. /// /// This function is the same as [`Validator::new`] except it also allows /// you to customize the active wasm features in use for validation. This /// can allow enabling experimental proposals or also turning off /// on-by-default wasm proposals. pubfn new_with_features(features: WasmFeatures) -> Validator { letmut ret = Validator::new();
ret.features = features;
ret
}
/// Returns the wasm features used for this validator. pubfn features(&self) -> &WasmFeatures {
&self.features
}
/// Reset this validator's state such that it is ready to validate a new /// Wasm module or component. /// /// This does *not* clear or reset the internal state keeping track of /// validated (and deduplicated and canonicalized) types, allowing you to /// use the same type identifiers (such as /// [`CoreTypeId`][crate::types::CoreTypeId]) for the same types that are /// defined multiple times across different modules and components. /// /// ``` /// fn foo() -> anyhow::Result<()> { /// use wasmparser::Validator; /// /// let mut validator = Validator::default(); /// /// // Two wasm modules, both of which define the same type, but at /// // different indices in their respective types index spaces. /// let wasm1 = wat::parse_str(" /// (module /// (type $same_type (func (param i32) (result f64))) /// ) /// ")?; /// let wasm2 = wat::parse_str(" /// (module /// (type $different_type (func)) /// (type $same_type (func (param i32) (result f64))) /// ) /// ")?; /// /// // Validate the first Wasm module and get the ID of its type. /// let types = validator.validate_all(&wasm1)?; /// let id1 = types.as_ref().core_type_at_in_module(0); /// /// // Reset the validator so we can parse the second wasm module inside /// // this validator's same context. /// validator.reset(); /// /// // Validate the second Wasm module and get the ID of its second type, /// // which is the same type as the first Wasm module's only type. /// let types = validator.validate_all(&wasm2)?; /// let id2 = types.as_ref().core_type_at_in_module(1); /// /// // Because both modules were processed in the same `Validator`, they /// // share the same types context and therefore the same type defined /// // multiple times across different modules will be deduplicated and /// // assigned the same identifier! /// assert_eq!(id1, id2); /// assert_eq!(types[id1], types[id2]); /// # Ok(()) /// # } /// # foo().unwrap() /// ``` pubfn reset(&mutself) { let Validator { // Not changing the identifier; users should be able to observe that // they are using the same validation context, even after resetting.
id: _,
// Don't mess with `types`, we specifically want to reuse canonicalizations.
types: _,
// Also leave features as they are. While this is perhaps not // strictly necessary, it helps us avoid weird bugs where we have // different views of what is or is not a valid type at different // times, despite using the same `TypeList` and hash consing // context, and therefore there could be moments in time where we // have "invalid" types inside our current types list.
features: _,
assert!(
matches!(state, State::End), "cannot reset a validator that did not successfully complete validation"
);
assert!(module.is_none()); #[cfg(feature = "component-model")]
assert!(components.is_empty());
*state = State::default();
}
/// Get this validator's unique identifier. /// /// Allows you to assert that you are always working with the same /// `Validator` instance, when you can't otherwise statically ensure that /// property by e.g. storing a reference to the validator inside your /// structure. pubfn id(&self) -> ValidatorId { self.id
}
/// Validates an entire in-memory module or component with this validator. /// /// This function will internally create a [`Parser`] to parse the `bytes` /// provided. The entire module or component specified by `bytes` will be /// parsed and validated. /// /// Upon success, the type information for the top-level module or component /// will be returned. pubfn validate_all(&mutself, bytes: &[u8]) -> Result<Types> { letmut functions_to_validate = Vec::new(); letmut last_types = None; letmut parser = Parser::new(0); let _ = &mut parser; #[cfg(feature = "features")]
parser.set_features(self.features); for payload in parser.parse_all(bytes) { matchself.payload(&payload?)? {
ValidPayload::Func(a, b) => {
functions_to_validate.push((a, b));
}
ValidPayload::End(types) => { // Only the last (top-level) type information will be returned
last_types = Some(types);
}
_ => {}
}
}
letmut allocs = FuncValidatorAllocations::default(); for (func, body) in functions_to_validate { letmut validator = func.into_validator(allocs);
validator.validate(&body)?;
allocs = validator.into_allocations();
}
Ok(last_types.unwrap())
}
/// Gets the types known by the validator so far within the /// module/component `level` modules/components up from the /// module/component currently being parsed. /// /// For instance, calling `validator.types(0)` will get the types of the /// module/component currently being parsed, and `validator.types(1)` will /// get the types of the component containing that module/component. /// /// Returns `None` if there is no module/component that many levels up. pubfn types(&self, mut level: usize) -> Option<TypesRef> { iflet Some(module) = &self.module { if level == 0 { return Some(TypesRef::from_module(self.id, &self.types, &module.module));
} else {
level -= 1; let _ = level;
}
}
/// Convenience function to validate a single [`Payload`]. /// /// This function is intended to be used as a convenience. It will /// internally perform any validation necessary to validate the [`Payload`] /// provided. The convenience part is that you're likely already going to /// be matching on [`Payload`] in your application, at which point it's more /// appropriate to call the individual methods on [`Validator`] per-variant /// in [`Payload`], such as [`Validator::type_section`]. /// /// This function returns a [`ValidPayload`] variant on success, indicating /// one of a few possible actions that need to be taken after a payload is /// validated. For example function contents are not validated here, they're /// returned through [`ValidPayload`] for validation by the caller. pubfn payload<'a>(&mut self, payload: &Payload<'a>) -> Result<ValidPayload<'a>> { usecrate::Payload::*; match payload {
Version {
num,
encoding,
range,
} => self.version(*num, *encoding, range)?,
/// Validates [`Payload::ImportSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn import_section(&mutself, section: &crate::ImportSectionReader<'_>) -> Result<()> { self.process_module_section(
Order::Import,
section, "import",
|state, _, _, count, offset| {
check_max(
state.module.imports.len(),
count,
MAX_WASM_IMPORTS, "imports",
offset,
)?;
state.module.assert_mut().imports.reserve(count as usize);
Ok(())
},
|state, features, types, import, offset| {
state
.module
.assert_mut()
.add_import(import, features, types, offset)
},
)
}
/// Validates [`Payload::FunctionSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn function_section(&mutself, section: &crate::FunctionSectionReader<'_>) -> Result<()> { self.process_module_section(
Order::Function,
section, "function",
|state, _, _, count, offset| {
check_max(
state.module.functions.len(),
count,
MAX_WASM_FUNCTIONS, "functions",
offset,
)?;
state.module.assert_mut().functions.reserve(count as usize);
debug_assert!(state.expected_code_bodies.is_none());
state.expected_code_bodies = Some(count);
Ok(())
},
|state, _, types, ty, offset| state.module.assert_mut().add_function(ty, types, offset),
)
}
/// Validates [`Payload::TableSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn table_section(&mutself, section: &>crate::TableSectionReader<'_>) -> Result<()> { let features = self.features; self.process_module_section(
Order::Table,
section, "table",
|state, _, _, count, offset| {
check_max(
state.module.tables.len(),
count,
state.module.max_tables(&features), "tables",
offset,
)?;
state.module.assert_mut().tables.reserve(count as usize);
Ok(())
},
|state, features, types, table, offset| state.add_table(table, features, types, offset),
)
}
/// Validates [`Payload::MemorySection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn memory_section(&mutself, section: &crate::MemorySectionReader<'_>) -> Result<()> { self.process_module_section(
Order::Memory,
section, "memory",
|state, features, _, count, offset| {
check_max(
state.module.memories.len(),
count,
state.module.max_memories(features), "memories",
offset,
)?;
state.module.assert_mut().memories.reserve(count as usize);
Ok(())
},
|state, features, _, ty, offset| {
state.module.assert_mut().add_memory(ty, features, offset)
},
)
}
/// Validates [`Payload::TagSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn tag_section(&mutself, section: &crate::TagSectionReader<'_>) -> Result<()> { if !self.features.exceptions() { return Err(BinaryReaderError::new( "exceptions proposal not enabled",
section.range().start,
));
}
/// Validates [`Payload::GlobalSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn global_section(&mutself, section: &crate::GlobalSectionReader<'_>) -> Result<()> { self.process_module_section(
Order::Global,
section, "global",
|state, _, _, count, offset| {
check_max(
state.module.globals.len(),
count,
MAX_WASM_GLOBALS, "globals",
offset,
)?;
state.module.assert_mut().globals.reserve(count as usize);
Ok(())
},
|state, features, types, global, offset| {
state.add_global(global, features, types, offset)
},
)
}
/// Validates [`Payload::ExportSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn export_section(&mutself, section: &crate::ExportSectionReader<'_>) -> Result<()> { self.process_module_section(
Order::Export,
section, "export",
|state, _, _, count, offset| {
check_max(
state.module.exports.len(),
count,
MAX_WASM_EXPORTS, "exports",
offset,
)?;
state.module.assert_mut().exports.reserve(count as usize);
Ok(())
},
|state, features, types, e, offset| { let state = state.module.assert_mut(); let ty = state.export_to_entity_type(&e, offset)?;
state.add_export(
e.name, ty, features, offset, false, /* checked above */
types,
)
},
)
}
/// Validates [`Payload::StartSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn start_section(&mutself, func: u32, range: &Range<usize>) -> Result<()> { let offset = range.start; self.state.ensure_module("start", offset)?; let state = self.module.as_mut().unwrap();
state.update_order(Order::Start, offset)?;
let ty = state.module.get_func_type(func, &self.types, offset)?; if !ty.params().is_empty() || !ty.results().is_empty() { return Err(BinaryReaderError::new( "invalid start function type",
offset,
));
}
Ok(())
}
/// Validates [`Payload::ElementSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn element_section(&mutself, section: &crate::ElementSectionReader<'_>) -> Result<()> { self.process_module_section(
Order::Element,
section, "element",
|state, _, _, count, offset| {
check_max(
state.module.element_types.len(),
count,
MAX_WASM_ELEMENT_SEGMENTS, "element segments",
offset,
)?;
state
.module
.assert_mut()
.element_types
.reserve(count as usize);
Ok(())
},
|state, features, types, e, offset| {
state.add_element_segment(e, features, types, offset)
},
)
}
/// Validates [`Payload::DataCountSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn data_count_section(&mutself, count: u32, range: &Range<usize>) -> Result<()> { let offset = range.start; self.state.ensure_module("data count", offset)?;
let state = self.module.as_mut().unwrap();
state.update_order(Order::DataCount, offset)?;
if count > MAX_WASM_DATA_SEGMENTS as u32 { return Err(BinaryReaderError::new( "data count section specifies too many data segments",
offset,
));
}
/// Validates [`Payload::CodeSectionStart`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn code_section_start(&mutself, count: u32, range: &Range<usize>) -> Result<()> { let offset = range.start; self.state.ensure_module("code", offset)?;
let state = self.module.as_mut().unwrap();
state.update_order(Order::Code, offset)?;
match state.expected_code_bodies.take() {
Some(n) if n == count => {}
Some(_) => { return Err(BinaryReaderError::new( "function and code section have inconsistent lengths",
offset,
));
} // empty code sections are allowed even if the function section is // missing
None if count == 0 => {}
None => { return Err(BinaryReaderError::new( "code section without function section",
offset,
))
}
}
// Take a snapshot of the types when we start the code section.
state.module.assert_mut().snapshot = Some(Arc::new(self.types.commit()));
Ok(())
}
/// Validates [`Payload::CodeSectionEntry`](crate::Payload). /// /// This function will prepare a [`FuncToValidate`] which can be used to /// create a [`FuncValidator`] to validate the function. The function body /// provided will not be parsed or validated by this function. /// /// Note that the returned [`FuncToValidate`] is "connected" to this /// [`Validator`] in that it uses the internal context of this validator for /// validating the function. The [`FuncToValidate`] can be sent to another /// thread, for example, to offload actual processing of functions /// elsewhere. /// /// This method should only be called when parsing a module. pubfn code_section_entry(
&mutself,
body: &crate::FunctionBody,
) -> Result<FuncToValidate<ValidatorResources>> { let offset = body.range().start; self.state.ensure_module("code", offset)?;
/// Validates [`Payload::DataSection`](crate::Payload). /// /// This method should only be called when parsing a module. pubfn data_section(&mutself, section: &crate::DataSectionReader<'_>) -> Result<()> { self.process_module_section(
Order::Data,
section, "data",
|state, _, _, count, offset| {
state.data_segment_count = count;
check_max(0, count, MAX_WASM_DATA_SEGMENTS, "data segments", offset)
},
|state, features, types, d, offset| state.add_data_segment(d, features, types, offset),
)
}
/// Validates [`Payload::ModuleSection`](crate::Payload). /// /// This method should only be called when parsing a component. #[cfg(feature = "component-model")] pubfn module_section(&mutself, range: &Range<usize>) -> Result<()> { self.state.ensure_component("module", range.start)?;
let current = self.components.last_mut().unwrap();
check_max(
current.core_modules.len(), 1,
MAX_WASM_MODULES, "modules",
range.start,
)?;
/// Validates [`Payload::InstanceSection`](crate::Payload). /// /// This method should only be called when parsing a component. #[cfg(feature = "component-model")] pubfn instance_section(&mutself, section: &crate::InstanceSectionReader) -> Result<()> { self.process_component_section(
section, "core instance",
|components, _, count, offset| { let current = components.last_mut().unwrap();
check_max(
current.instance_count(),
count,
MAX_WASM_INSTANCES, "instances",
offset,
)?;
current.core_instances.reserve(count as usize);
Ok(())
},
|components, types, _, instance, offset| {
components
.last_mut()
.unwrap()
.add_core_instance(instance, types, offset)
},
)
}
/// Validates [`Payload::CoreTypeSection`](crate::Payload). /// /// This method should only be called when parsing a component. #[cfg(feature = "component-model")] pubfn core_type_section(&mutself, section: &crate::CoreTypeSectionReader<'_>) -> Result<()> { self.process_component_section(
section, "core type",
|components, _types, count, offset| { let current = components.last_mut().unwrap();
check_max(current.type_count(), count, MAX_WASM_TYPES, "types", offset)?;
current.core_types.reserve(count as usize);
Ok(())
},
|components, types, features, ty, offset| {
ComponentState::add_core_type(
components, ty, features, types, offset, false, /* checked above */
)
},
)
}
/// Validates [`Payload::ComponentSection`](crate::Payload). /// /// This method should only be called when parsing a component. #[cfg(feature = "component-model")] pubfn component_section(&mutself, range: &Range<usize>) -> Result<()> { self.state.ensure_component("component", range.start)?;
let current = self.components.last_mut().unwrap();
check_max(
current.components.len(), 1,
MAX_WASM_COMPONENTS, "components",
range.start,
)?;
/// Validates [`Payload::ComponentStartSection`](crate::Payload). /// /// This method should only be called when parsing a component. #[cfg(feature = "component-model")] pubfn component_start_section(
&mutself,
f: &crate::ComponentStartFunction,
range: &Range<usize>,
) -> Result<()> { self.state.ensure_component("start", range.start)?;
/// Validates [`Payload::End`](crate::Payload). /// /// Returns the types known to the validator for the module or component. pubfn end(&mutself, offset: usize) -> Result<Types> { match mem::replace(&mutself.state, State::End) {
State::Unparsed(_) => Err(BinaryReaderError::new( "cannot call `end` before a header has been parsed",
offset,
)),
State::End => Err(BinaryReaderError::new( "cannot call `end` after parsing has completed",
offset,
)),
State::Module => { letmut state = self.module.take().unwrap();
state.validate_end(offset)?;
// If there's a parent component, we'll add a module to the parent state // and continue to validate the component #[cfg(feature = "component-model")] iflet Some(parent) = self.components.last_mut() {
parent.add_core_module(&state.module, &mutself.types, offset)?; self.state = State::Component;
}
// Validate that all values were used for the component iflet Some(index) = component.values.iter().position(|(_, used)| !*used) {
bail!(
offset, "value index {index} was not used as part of an \
instantiation, start function, or export"
);
}
// If there's a parent component, pop the stack, add it to the parent, // and continue to validate the component let ty = component.finish(&mutself.types, offset)?; iflet Some(parent) = self.components.last_mut() {
parent.add_component(ty, &mutself.types)?; self.state = State::Component;
}
let id = types.core_type_at_in_module(0); let ty = types[id].unwrap_func();
assert_eq!(ty.params(), [ValType::I32, ValType::I64]);
assert_eq!(ty.results(), [ValType::I32]);
let id = types.core_type_at_in_module(1); let ty = types[id].unwrap_func();
assert_eq!(ty.params(), [ValType::I64, ValType::I32]);
assert_eq!(ty.results(), []);
let id = types.core_function_at(0); let ty = types[id].unwrap_func();
assert_eq!(ty.params(), [ValType::I32, ValType::I64]);
assert_eq!(ty.results(), [ValType::I32]);
let ty = types.tag_at(0); let ty = types[ty].unwrap_func();
assert_eq!(ty.params(), [ValType::I64, ValType::I32]);
assert_eq!(ty.results(), []);
let types = validator.validate_all(&bytes)?; let types = types.as_ref();
let t_id = types.component_defined_type_at(0); let a1_id = types.component_defined_type_at(1); let a2_id = types.component_defined_type_at(2);
// The ids should all be the same
assert!(t_id == a1_id);
assert!(t_id == a2_id);
assert!(a1_id == a2_id);
// However, they should all point to the same type
assert!(std::ptr::eq(&types[t_id], &types[a1_id],));
assert!(std::ptr::eq(&types[t_id], &types[a2_id],));
let types = validator.validate_all(&bytes)?; let types = types.as_ref();
let t_id = types.component_defined_type_at(0); let a1_id = types.component_defined_type_at(1); let a2_id = types.component_defined_type_at(2);
// The ids should all be the same
assert!(t_id != a1_id);
assert!(t_id != a2_id);
assert!(a1_id != a2_id);
// However, they should all point to the same type
assert!(std::ptr::eq(&types[t_id], &types[a1_id],));
assert!(std::ptr::eq(&types[t_id], &types[a2_id],));
Ok(())
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet am 2026-06-18)
¤