Skip to content

Commit

Permalink
Add a component-model feature to wasmparser (#1845)
Browse files Browse the repository at this point in the history
* Split-out component-related types in `wasmparser`

This commit splits the `types.rs` in `wasmparser` into a `types.rs` and
`component_types.rs`. The goal here is to split all component-related
logic to a separate file instead of having everything bundled together.
This will make it easier to conditionally include component-related bits
in the future and additionally make each module more understandable by
focusing only on one piece of functionality.

* Make `component_types` a part of `wasmparser`'s API

Don't `pub use` all the types from that module into the preexisting
`types` module. This helps reduce the cognitive load when looking at the
`types` module by keeping all the component bits in one place.

* Add a `component-model` feature to `wasmparser`

This commit gates the implementation of the component-model proposal in
`wasmparser` behind a Cargo feature named `component-model`. This is
intended to lower the binary size and compile time of this crate in
situations where components are not needed. Additionally this helps
factor logic cleanly between core module and component layers both at
the API and internal levels.

* Add some more checks to CI

* Fix fuzzer and bench build

* Review feedback
  • Loading branch information
alexcrichton authored Oct 7, 2024
1 parent ac29377 commit d12d533
Show file tree
Hide file tree
Showing 32 changed files with 3,821 additions and 3,412 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ jobs:
- run: cargo check --no-default-features -p wasmparser --features no-hash-maps
- run: cargo check --no-default-features -p wasmparser --features serde
- run: cargo check --no-default-features -p wasmparser --features serde,no-hash-maps
- run: cargo check --no-default-features -p wasmparser --features component-model
- run: cargo check --no-default-features -p wasmparser --features component-model,validate
- run: cargo check --no-default-features -p wasmparser --features std,component-model
- run: cargo check --no-default-features -p wasmparser --features std,component-model,validate
- run: cargo check --no-default-features -p wast
- run: cargo check --no-default-features -p wast --features wasm-module
- run: cargo check --no-default-features -p wast --features wasm-module,component-model
Expand Down
3 changes: 2 additions & 1 deletion crates/wasm-compose/src/composer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use anyhow::{anyhow, bail, Context, Result};
use indexmap::IndexMap;
use std::{collections::VecDeque, ffi::OsStr, path::Path};
use wasmparser::{
types::{ComponentEntityType, ComponentInstanceTypeId, TypesRef},
component_types::{ComponentEntityType, ComponentInstanceTypeId},
types::TypesRef,
ComponentExternalKind, ComponentTypeRef,
};

Expand Down
115 changes: 51 additions & 64 deletions crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ use std::collections::{hash_map::Entry, HashMap};
use std::mem;
use wasm_encoder::*;
use wasmparser::{
names::KebabString,
types::{
self, AnyTypeId, ComponentAnyTypeId, ComponentCoreModuleTypeId, ComponentCoreTypeId,
ComponentDefinedTypeId, ComponentEntityType, ComponentFuncTypeId, ComponentInstanceTypeId,
ComponentTypeId, Remap, Remapping, ResourceId, SubtypeCx,
component_types::{
self as ct, AnyTypeId, ComponentAnyTypeId, ComponentCoreModuleTypeId, ComponentCoreTypeId,
ComponentDefinedType, ComponentDefinedTypeId, ComponentEntityType, ComponentFuncTypeId,
ComponentInstanceTypeId, ComponentTypeId, RecordType, Remap, Remapping, ResourceId,
SubtypeCx, TupleType, VariantType,
},
ComponentExternalKind,
names::KebabString,
types, ComponentExternalKind,
};

fn type_ref_to_export_kind(ty: wasmparser::ComponentTypeRef) -> ComponentExportKind {
Expand Down Expand Up @@ -269,8 +270,8 @@ impl<'a> TypeEncoder<'a> {
exports: E,
) -> ComponentType
where
I: IntoIterator<Item = (&'a str, wasmparser::types::ComponentEntityType)>,
E: IntoIterator<Item = (&'a str, wasmparser::types::ComponentEntityType)>,
I: IntoIterator<Item = (&'a str, ComponentEntityType)>,
E: IntoIterator<Item = (&'a str, ComponentEntityType)>,
{
state.push(Encodable::Component(ComponentType::new()));

Expand Down Expand Up @@ -300,7 +301,7 @@ impl<'a> TypeEncoder<'a> {

pub fn instance<E>(&self, state: &mut TypeState<'a>, exports: E) -> InstanceType
where
E: IntoIterator<Item = (&'a str, wasmparser::types::ComponentEntityType)>,
E: IntoIterator<Item = (&'a str, ComponentEntityType)>,
{
state.push(Encodable::Instance(InstanceType::new()));

Expand Down Expand Up @@ -390,19 +391,15 @@ impl<'a> TypeEncoder<'a> {
fn component_entity_type(
&self,
state: &mut TypeState<'a>,
ty: wasmparser::types::ComponentEntityType,
ty: ComponentEntityType,
) -> ComponentTypeRef {
match ty {
wasmparser::types::ComponentEntityType::Module(id) => {
ComponentTypeRef::Module(self.ty(state, id.into()))
}
wasmparser::types::ComponentEntityType::Func(id) => {
ComponentTypeRef::Func(self.ty(state, id.into()))
}
wasmparser::types::ComponentEntityType::Value(ty) => {
ComponentEntityType::Module(id) => ComponentTypeRef::Module(self.ty(state, id.into())),
ComponentEntityType::Func(id) => ComponentTypeRef::Func(self.ty(state, id.into())),
ComponentEntityType::Value(ty) => {
ComponentTypeRef::Value(self.component_val_type(state, ty))
}
wasmparser::types::ComponentEntityType::Type {
ComponentEntityType::Type {
created: created @ ComponentAnyTypeId::Resource(_),
referenced,
} => {
Expand All @@ -414,13 +411,13 @@ impl<'a> TypeEncoder<'a> {
ComponentTypeRef::Type(TypeBounds::Eq(self.ty(state, referenced.into())))
}
}
wasmparser::types::ComponentEntityType::Type { referenced, .. } => {
ComponentEntityType::Type { referenced, .. } => {
ComponentTypeRef::Type(TypeBounds::Eq(self.ty(state, referenced.into())))
}
wasmparser::types::ComponentEntityType::Instance(id) => {
ComponentEntityType::Instance(id) => {
ComponentTypeRef::Instance(self.ty(state, id.into()))
}
wasmparser::types::ComponentEntityType::Component(id) => {
ComponentEntityType::Component(id) => {
ComponentTypeRef::Component(self.ty(state, id.into()))
}
}
Expand Down Expand Up @@ -626,13 +623,11 @@ impl<'a> TypeEncoder<'a> {
fn component_val_type(
&self,
state: &mut TypeState<'a>,
ty: wasmparser::types::ComponentValType,
ty: ct::ComponentValType,
) -> ComponentValType {
match ty {
wasmparser::types::ComponentValType::Primitive(ty) => {
ComponentValType::Primitive(ty.into())
}
wasmparser::types::ComponentValType::Type(id) => {
ct::ComponentValType::Primitive(ty) => ComponentValType::Primitive(ty.into()),
ct::ComponentValType::Type(id) => {
ComponentValType::Type(self.ty(state, ComponentAnyTypeId::from(id).into()))
}
}
Expand All @@ -642,7 +637,7 @@ impl<'a> TypeEncoder<'a> {
let ty = &self.0.types[id];

match ty {
wasmparser::types::ComponentDefinedType::Primitive(ty) => {
ComponentDefinedType::Primitive(ty) => {
let index = state.cur.encodable.type_count();
state
.cur
Expand All @@ -652,27 +647,21 @@ impl<'a> TypeEncoder<'a> {
.primitive((*ty).into());
index
}
wasmparser::types::ComponentDefinedType::Record(r) => self.record(state, r),
wasmparser::types::ComponentDefinedType::Variant(v) => self.variant(state, v),
wasmparser::types::ComponentDefinedType::List(ty) => self.list(state, *ty),
wasmparser::types::ComponentDefinedType::Tuple(t) => self.tuple(state, t),
wasmparser::types::ComponentDefinedType::Flags(names) => {
Self::flags(&mut state.cur.encodable, names)
}
wasmparser::types::ComponentDefinedType::Enum(cases) => {
Self::enum_type(&mut state.cur.encodable, cases)
}
wasmparser::types::ComponentDefinedType::Option(ty) => self.option(state, *ty),
wasmparser::types::ComponentDefinedType::Result { ok, err } => {
self.result(state, *ok, *err)
}
wasmparser::types::ComponentDefinedType::Own(r) => {
ComponentDefinedType::Record(r) => self.record(state, r),
ComponentDefinedType::Variant(v) => self.variant(state, v),
ComponentDefinedType::List(ty) => self.list(state, *ty),
ComponentDefinedType::Tuple(t) => self.tuple(state, t),
ComponentDefinedType::Flags(names) => Self::flags(&mut state.cur.encodable, names),
ComponentDefinedType::Enum(cases) => Self::enum_type(&mut state.cur.encodable, cases),
ComponentDefinedType::Option(ty) => self.option(state, *ty),
ComponentDefinedType::Result { ok, err } => self.result(state, *ok, *err),
ComponentDefinedType::Own(r) => {
let ty = self.ty(state, (*r).into());
let index = state.cur.encodable.type_count();
state.cur.encodable.ty().defined_type().own(ty);
index
}
wasmparser::types::ComponentDefinedType::Borrow(r) => {
ComponentDefinedType::Borrow(r) => {
let ty = self.ty(state, (*r).into());
let index = state.cur.encodable.type_count();
state.cur.encodable.ty().defined_type().borrow(ty);
Expand All @@ -681,7 +670,7 @@ impl<'a> TypeEncoder<'a> {
}
}

fn record(&self, state: &mut TypeState<'a>, record: &wasmparser::types::RecordType) -> u32 {
fn record(&self, state: &mut TypeState<'a>, record: &RecordType) -> u32 {
let fields = record
.fields
.iter()
Expand All @@ -693,7 +682,7 @@ impl<'a> TypeEncoder<'a> {
index
}

fn variant(&self, state: &mut TypeState<'a>, variant: &wasmparser::types::VariantType) -> u32 {
fn variant(&self, state: &mut TypeState<'a>, variant: &VariantType) -> u32 {
let cases = variant
.cases
.iter()
Expand All @@ -712,14 +701,14 @@ impl<'a> TypeEncoder<'a> {
index
}

fn list(&self, state: &mut TypeState<'a>, ty: wasmparser::types::ComponentValType) -> u32 {
fn list(&self, state: &mut TypeState<'a>, ty: ct::ComponentValType) -> u32 {
let ty = self.component_val_type(state, ty);
let index = state.cur.encodable.type_count();
state.cur.encodable.ty().defined_type().list(ty);
index
}

fn tuple(&self, state: &mut TypeState<'a>, tuple: &wasmparser::types::TupleType) -> u32 {
fn tuple(&self, state: &mut TypeState<'a>, tuple: &TupleType) -> u32 {
let types = tuple
.types
.iter()
Expand Down Expand Up @@ -754,7 +743,7 @@ impl<'a> TypeEncoder<'a> {
index
}

fn option(&self, state: &mut TypeState<'a>, ty: wasmparser::types::ComponentValType) -> u32 {
fn option(&self, state: &mut TypeState<'a>, ty: ct::ComponentValType) -> u32 {
let ty = self.component_val_type(state, ty);

let index = state.cur.encodable.type_count();
Expand All @@ -765,8 +754,8 @@ impl<'a> TypeEncoder<'a> {
fn result(
&self,
state: &mut TypeState<'a>,
ok: Option<wasmparser::types::ComponentValType>,
err: Option<wasmparser::types::ComponentValType>,
ok: Option<ct::ComponentValType>,
err: Option<ct::ComponentValType>,
) -> u32 {
let ok = ok.map(|ty| self.component_val_type(state, ty));
let err = err.map(|ty| self.component_val_type(state, ty));
Expand Down Expand Up @@ -1190,10 +1179,10 @@ impl DependencyRegistrar<'_, '_> {
}
}

fn val_type(&mut self, ty: types::ComponentValType) {
fn val_type(&mut self, ty: ct::ComponentValType) {
match ty {
types::ComponentValType::Type(t) => self.ty(t.into()),
types::ComponentValType::Primitive(_) => {}
ct::ComponentValType::Type(t) => self.ty(t.into()),
ct::ComponentValType::Primitive(_) => {}
}
}

Expand Down Expand Up @@ -1224,33 +1213,31 @@ impl DependencyRegistrar<'_, '_> {

fn defined(&mut self, ty: ComponentDefinedTypeId) {
match &self.types[ty] {
types::ComponentDefinedType::Primitive(_)
| types::ComponentDefinedType::Enum(_)
| types::ComponentDefinedType::Flags(_) => {}
types::ComponentDefinedType::List(t) | types::ComponentDefinedType::Option(t) => {
self.val_type(*t)
}
types::ComponentDefinedType::Own(r) | types::ComponentDefinedType::Borrow(r) => {
ComponentDefinedType::Primitive(_)
| ComponentDefinedType::Enum(_)
| ComponentDefinedType::Flags(_) => {}
ComponentDefinedType::List(t) | ComponentDefinedType::Option(t) => self.val_type(*t),
ComponentDefinedType::Own(r) | ComponentDefinedType::Borrow(r) => {
self.ty(ComponentAnyTypeId::Resource(*r))
}
types::ComponentDefinedType::Record(r) => {
ComponentDefinedType::Record(r) => {
for (_, ty) in r.fields.iter() {
self.val_type(*ty);
}
}
types::ComponentDefinedType::Tuple(r) => {
ComponentDefinedType::Tuple(r) => {
for ty in r.types.iter() {
self.val_type(*ty);
}
}
types::ComponentDefinedType::Variant(r) => {
ComponentDefinedType::Variant(r) => {
for (_, case) in r.cases.iter() {
if let Some(ty) = case.ty {
self.val_type(ty);
}
}
}
types::ComponentDefinedType::Result { ok, err } => {
ComponentDefinedType::Result { ok, err } => {
if let Some(ok) = ok {
self.val_type(*ok);
}
Expand Down
7 changes: 4 additions & 3 deletions crates/wasm-compose/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ use std::{
sync::atomic::{AtomicUsize, Ordering},
};
use wasmparser::{
names::ComponentName,
types::{
component_types::{
ComponentAnyTypeId, ComponentEntityType, ComponentInstanceTypeId, Remap, Remapping,
ResourceId, SubtypeCx, Types, TypesRef,
ResourceId, SubtypeCx,
},
names::ComponentName,
types::{Types, TypesRef},
Chunk, ComponentExternalKind, ComponentTypeRef, Encoding, Parser, Payload, ValidPayload,
Validator,
};
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-encoder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ default = ['component-model']

# On-by-default: conditional support for emitting components in addition to
# core modules.
component-model = []
component-model = ['wasmparser?/component-model']
29 changes: 21 additions & 8 deletions crates/wasm-encoder/src/reencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ impl Reencode for RoundtripReencoder {
pub mod utils {
use super::{Error, Reencode};
use crate::{CoreTypeEncoder, Encode};
use std::ops::Range;

pub fn parse_core_module<T: ?Sized + Reencode>(
reencoder: &mut T,
Expand All @@ -620,7 +621,17 @@ pub mod utils {
reencoder.intersperse_section_hook(module, after, before)
}

// Convert from `range` to a byte range within `data` while
// accounting for various offsets. Then create a
// `CodeSectionReader` (which notably the payload does not
// give us here) and recurse with that. This means that
// users overridding `parse_code_section` always get that
// function called.
let orig_offset = parser.offset() as usize;
let get_original_section = |range: Range<usize>| {
data.get(range.start - orig_offset..range.end - orig_offset)
.ok_or(Error::InvalidCodeSectionSize)
};
let mut last_section = None;

for section in parser.parse_all(data) {
Expand Down Expand Up @@ -778,11 +789,7 @@ pub mod utils {
// give us here) and recurse with that. This means that
// users overridding `parse_code_section` always get that
// function called.
let section = match data.get(range.start - orig_offset..range.end - orig_offset)
{
Some(section) => section,
None => return Err(Error::InvalidCodeSectionSize),
};
let section = get_original_section(range.clone())?;
let reader = wasmparser::BinaryReader::new(section, range.start);
let section = wasmparser::CodeSectionReader::new(reader)?;
reencoder.parse_code_section(&mut codes, section)?;
Expand All @@ -794,6 +801,7 @@ pub mod utils {
// that we just skip all these payloads.
wasmparser::Payload::CodeSectionEntry(_) => {}

#[cfg(feature = "component-model")]
wasmparser::Payload::ModuleSection { .. }
| wasmparser::Payload::InstanceSection(_)
| wasmparser::Payload::CoreTypeSection(_)
Expand All @@ -810,12 +818,17 @@ pub mod utils {
wasmparser::Payload::CustomSection(section) => {
reencoder.parse_custom_section(module, section)?;
}
wasmparser::Payload::UnknownSection { id, contents, .. } => {
reencoder.parse_unknown_section(module, id, contents)?;
}
wasmparser::Payload::End(_) => {
handle_intersperse_section_hook(reencoder, module, &mut last_section, None)?;
}

other => match other.as_section() {
Some((id, range)) => {
let section = get_original_section(range)?;
reencoder.parse_unknown_section(module, id, section)?;
}
None => unreachable!(),
},
}
}

Expand Down
11 changes: 8 additions & 3 deletions crates/wasm-encoder/src/reencode/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,6 @@ pub mod component_utils {
wasmparser::Payload::CustomSection(section) => {
reencoder.parse_component_custom_section(component, section)?;
}
wasmparser::Payload::UnknownSection { id, contents, .. } => {
reencoder.parse_unknown_component_section(component, id, contents)?;
}
wasmparser::Payload::ModuleSection {
parser,
unchecked_range,
Expand All @@ -521,6 +518,14 @@ pub mod component_utils {
reencoder.parse_component_start_section(component, start)?;
}
wasmparser::Payload::End(_) => {}

other => match other.as_section() {
Some((id, range)) => {
let section = &whole_component[range];
reencoder.parse_unknown_component_section(component, id, section)?;
}
None => unreachable!(),
},
}
Ok(())
}
Expand Down
Loading

0 comments on commit d12d533

Please sign in to comment.