use crate::component::{Export, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS};
use crate::{
CompiledModuleInfo, EntityType, ModuleTypes, ModuleTypesBuilder, PrimaryMap, TypeConvert,
WasmHeapType, WasmValType,
};
use anyhow::{bail, Result};
use cranelift_entity::EntityRef;
use indexmap::{IndexMap, IndexSet};
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Index;
use wasmparser::names::KebabString;
use wasmparser::types;
use wasmtime_component_util::{DiscriminantSize, FlagsSize};
use wasmtime_types::ModuleInternedTypeIndex;
pub use wasmtime_types::StaticModuleIndex;
mod resources;
pub use resources::ResourcesBuilder;
const MAX_TYPE_DEPTH: u32 = 100;
macro_rules! indices {
($(
$(#[$a:meta])*
pub struct $name:ident(u32);
)*) => ($(
$(#[$a])*
#[derive(
Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug,
Serialize, Deserialize,
)]
#[repr(transparent)]
pub struct $name(u32);
cranelift_entity::entity_impl!($name);
)*);
}
indices! {
pub struct ComponentTypeIndex(u32);
pub struct ModuleIndex(u32);
pub struct ComponentIndex(u32);
pub struct ModuleInstanceIndex(u32);
pub struct ComponentInstanceIndex(u32);
pub struct ComponentFuncIndex(u32);
pub struct TypeComponentIndex(u32);
pub struct TypeComponentInstanceIndex(u32);
pub struct TypeModuleIndex(u32);
pub struct TypeFuncIndex(u32);
pub struct TypeRecordIndex(u32);
pub struct TypeVariantIndex(u32);
pub struct TypeTupleIndex(u32);
pub struct TypeFlagsIndex(u32);
pub struct TypeEnumIndex(u32);
pub struct TypeOptionIndex(u32);
pub struct TypeResultIndex(u32);
pub struct TypeListIndex(u32);
pub struct TypeResourceTableIndex(u32);
pub struct ResourceIndex(u32);
pub struct DefinedResourceIndex(u32);
pub struct ModuleUpvarIndex(u32);
pub struct ComponentUpvarIndex(u32);
pub struct StaticComponentIndex(u32);
pub struct RuntimeInstanceIndex(u32);
pub struct RuntimeComponentInstanceIndex(u32);
pub struct ImportIndex(u32);
pub struct RuntimeImportIndex(u32);
pub struct LoweredIndex(u32);
pub struct RuntimeMemoryIndex(u32);
pub struct RuntimeReallocIndex(u32);
pub struct RuntimePostReturnIndex(u32);
pub struct TrampolineIndex(u32);
}
pub use crate::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
#[derive(Debug, Clone, Copy)]
#[allow(missing_docs)]
pub enum ComponentItem {
Func(ComponentFuncIndex),
Module(ModuleIndex),
Component(ComponentIndex),
ComponentInstance(ComponentInstanceIndex),
Type(types::ComponentAnyTypeId),
}
#[derive(Default, Serialize, Deserialize)]
pub struct ComponentTypes {
modules: PrimaryMap<TypeModuleIndex, TypeModule>,
components: PrimaryMap<TypeComponentIndex, TypeComponent>,
component_instances: PrimaryMap<TypeComponentInstanceIndex, TypeComponentInstance>,
functions: PrimaryMap<TypeFuncIndex, TypeFunc>,
lists: PrimaryMap<TypeListIndex, TypeList>,
records: PrimaryMap<TypeRecordIndex, TypeRecord>,
variants: PrimaryMap<TypeVariantIndex, TypeVariant>,
tuples: PrimaryMap<TypeTupleIndex, TypeTuple>,
enums: PrimaryMap<TypeEnumIndex, TypeEnum>,
flags: PrimaryMap<TypeFlagsIndex, TypeFlags>,
options: PrimaryMap<TypeOptionIndex, TypeOption>,
results: PrimaryMap<TypeResultIndex, TypeResult>,
resource_tables: PrimaryMap<TypeResourceTableIndex, TypeResourceTable>,
module_types: ModuleTypes,
}
impl ComponentTypes {
pub fn module_types(&self) -> &ModuleTypes {
&self.module_types
}
pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo {
match ty {
InterfaceType::U8 | InterfaceType::S8 | InterfaceType::Bool => {
&CanonicalAbiInfo::SCALAR1
}
InterfaceType::U16 | InterfaceType::S16 => &CanonicalAbiInfo::SCALAR2,
InterfaceType::U32
| InterfaceType::S32
| InterfaceType::Float32
| InterfaceType::Char
| InterfaceType::Own(_)
| InterfaceType::Borrow(_) => &CanonicalAbiInfo::SCALAR4,
InterfaceType::U64 | InterfaceType::S64 | InterfaceType::Float64 => {
&CanonicalAbiInfo::SCALAR8
}
InterfaceType::String | InterfaceType::List(_) => &CanonicalAbiInfo::POINTER_PAIR,
InterfaceType::Record(i) => &self[*i].abi,
InterfaceType::Variant(i) => &self[*i].abi,
InterfaceType::Tuple(i) => &self[*i].abi,
InterfaceType::Flags(i) => &self[*i].abi,
InterfaceType::Enum(i) => &self[*i].abi,
InterfaceType::Option(i) => &self[*i].abi,
InterfaceType::Result(i) => &self[*i].abi,
}
}
}
macro_rules! impl_index {
($(impl Index<$ty:ident> for ComponentTypes { $output:ident => $field:ident })*) => ($(
impl std::ops::Index<$ty> for ComponentTypes {
type Output = $output;
#[inline]
fn index(&self, idx: $ty) -> &$output {
&self.$field[idx]
}
}
impl std::ops::Index<$ty> for ComponentTypesBuilder {
type Output = $output;
#[inline]
fn index(&self, idx: $ty) -> &$output {
&self.component_types[idx]
}
}
)*)
}
impl_index! {
impl Index<TypeModuleIndex> for ComponentTypes { TypeModule => modules }
impl Index<TypeComponentIndex> for ComponentTypes { TypeComponent => components }
impl Index<TypeComponentInstanceIndex> for ComponentTypes { TypeComponentInstance => component_instances }
impl Index<TypeFuncIndex> for ComponentTypes { TypeFunc => functions }
impl Index<TypeRecordIndex> for ComponentTypes { TypeRecord => records }
impl Index<TypeVariantIndex> for ComponentTypes { TypeVariant => variants }
impl Index<TypeTupleIndex> for ComponentTypes { TypeTuple => tuples }
impl Index<TypeEnumIndex> for ComponentTypes { TypeEnum => enums }
impl Index<TypeFlagsIndex> for ComponentTypes { TypeFlags => flags }
impl Index<TypeOptionIndex> for ComponentTypes { TypeOption => options }
impl Index<TypeResultIndex> for ComponentTypes { TypeResult => results }
impl Index<TypeListIndex> for ComponentTypes { TypeList => lists }
impl Index<TypeResourceTableIndex> for ComponentTypes { TypeResourceTable => resource_tables }
}
impl<T> Index<T> for ComponentTypes
where
ModuleTypes: Index<T>,
{
type Output = <ModuleTypes as Index<T>>::Output;
fn index(&self, idx: T) -> &Self::Output {
self.module_types.index(idx)
}
}
impl<T> Index<T> for ComponentTypesBuilder
where
ModuleTypes: Index<T>,
{
type Output = <ModuleTypes as Index<T>>::Output;
fn index(&self, idx: T) -> &Self::Output {
self.module_types.index(idx)
}
}
#[derive(Default)]
pub struct ComponentTypesBuilder {
functions: HashMap<TypeFunc, TypeFuncIndex>,
lists: HashMap<TypeList, TypeListIndex>,
records: HashMap<TypeRecord, TypeRecordIndex>,
variants: HashMap<TypeVariant, TypeVariantIndex>,
tuples: HashMap<TypeTuple, TypeTupleIndex>,
enums: HashMap<TypeEnum, TypeEnumIndex>,
flags: HashMap<TypeFlags, TypeFlagsIndex>,
options: HashMap<TypeOption, TypeOptionIndex>,
results: HashMap<TypeResult, TypeResultIndex>,
component_types: ComponentTypes,
module_types: ModuleTypesBuilder,
type_info: TypeInformationCache,
resources: ResourcesBuilder,
}
macro_rules! intern_and_fill_flat_types {
($me:ident, $name:ident, $val:ident) => {{
if let Some(idx) = $me.$name.get(&$val) {
return *idx;
}
let idx = $me.component_types.$name.push($val.clone());
let mut info = TypeInformation::new();
info.$name($me, &$val);
let idx2 = $me.type_info.$name.push(info);
assert_eq!(idx, idx2);
$me.$name.insert($val, idx);
return idx;
}};
}
impl ComponentTypesBuilder {
fn export_type_def(
&mut self,
static_modules: &PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
ty: &Export,
) -> TypeDef {
match ty {
Export::LiftedFunction { ty, .. } => TypeDef::ComponentFunc(*ty),
Export::ModuleStatic(idx) => {
let mut module_ty = TypeModule::default();
let module = &static_modules[*idx].module;
for (namespace, name, ty) in module.imports() {
module_ty
.imports
.insert((namespace.to_string(), name.to_string()), ty);
}
for (name, ty) in module.exports.iter() {
module_ty
.exports
.insert(name.to_string(), module.type_of(*ty));
}
TypeDef::Module(self.component_types.modules.push(module_ty))
}
Export::ModuleImport { ty, .. } => TypeDef::Module(*ty),
Export::Instance { ty: Some(ty), .. } => TypeDef::ComponentInstance(*ty),
Export::Instance { exports, .. } => {
let mut instance_ty = TypeComponentInstance::default();
for (name, ty) in exports {
instance_ty
.exports
.insert(name.to_string(), self.export_type_def(static_modules, ty));
}
TypeDef::ComponentInstance(
self.component_types.component_instances.push(instance_ty),
)
}
Export::Type(ty) => *ty,
}
}
pub fn finish<'a>(
mut self,
static_modules: &PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
imports: impl IntoIterator<Item = (String, TypeDef)>,
exports: impl IntoIterator<Item = (String, &'a Export)>,
) -> (ComponentTypes, TypeComponentIndex) {
let mut component_ty = TypeComponent::default();
for (name, ty) in imports {
component_ty.imports.insert(name, ty);
}
for (name, ty) in exports {
component_ty
.exports
.insert(name, self.export_type_def(static_modules, ty));
}
let ty = self.component_types.components.push(component_ty);
self.component_types.module_types = self.module_types.finish();
(self.component_types, ty)
}
pub fn find_resource_drop_signature(&self) -> Option<ModuleInternedTypeIndex> {
self.module_types
.wasm_signatures()
.find(|(_, sig)| {
sig.params().len() == 1
&& sig.returns().len() == 0
&& sig.params()[0] == WasmValType::I32
})
.map(|(i, _)| i)
}
pub fn module_types_builder(&self) -> &ModuleTypesBuilder {
&self.module_types
}
pub fn module_types_builder_mut(&mut self) -> &mut ModuleTypesBuilder {
&mut self.module_types
}
pub fn num_resource_tables(&self) -> usize {
self.component_types.resource_tables.len()
}
pub fn resources_mut(&mut self) -> &mut ResourcesBuilder {
&mut self.resources
}
pub fn resources_mut_and_types(&mut self) -> (&mut ResourcesBuilder, &ComponentTypes) {
(&mut self.resources, &self.component_types)
}
pub fn convert_component_func_type(
&mut self,
types: types::TypesRef<'_>,
id: types::ComponentFuncTypeId,
) -> Result<TypeFuncIndex> {
let ty = &types[id];
let params = ty
.params
.iter()
.map(|(_name, ty)| self.valtype(types, ty))
.collect::<Result<_>>()?;
let results = ty
.results
.iter()
.map(|(_name, ty)| self.valtype(types, ty))
.collect::<Result<_>>()?;
let ty = TypeFunc {
params: self.new_tuple_type(params),
results: self.new_tuple_type(results),
};
Ok(self.add_func_type(ty))
}
pub fn convert_component_entity_type(
&mut self,
types: types::TypesRef<'_>,
ty: types::ComponentEntityType,
) -> Result<TypeDef> {
Ok(match ty {
types::ComponentEntityType::Module(id) => {
TypeDef::Module(self.convert_module(types, id)?)
}
types::ComponentEntityType::Component(id) => {
TypeDef::Component(self.convert_component(types, id)?)
}
types::ComponentEntityType::Instance(id) => {
TypeDef::ComponentInstance(self.convert_instance(types, id)?)
}
types::ComponentEntityType::Func(id) => {
TypeDef::ComponentFunc(self.convert_component_func_type(types, id)?)
}
types::ComponentEntityType::Type { created, .. } => match created {
types::ComponentAnyTypeId::Defined(id) => {
TypeDef::Interface(self.defined_type(types, id)?)
}
types::ComponentAnyTypeId::Resource(id) => {
TypeDef::Resource(self.resource_id(id.resource()))
}
_ => bail!("unsupported type export"),
},
types::ComponentEntityType::Value(_) => bail!("values not supported"),
})
}
pub fn convert_type(
&mut self,
types: types::TypesRef<'_>,
id: types::ComponentAnyTypeId,
) -> Result<TypeDef> {
Ok(match id {
types::ComponentAnyTypeId::Defined(id) => {
TypeDef::Interface(self.defined_type(types, id)?)
}
types::ComponentAnyTypeId::Component(id) => {
TypeDef::Component(self.convert_component(types, id)?)
}
types::ComponentAnyTypeId::Instance(id) => {
TypeDef::ComponentInstance(self.convert_instance(types, id)?)
}
types::ComponentAnyTypeId::Func(id) => {
TypeDef::ComponentFunc(self.convert_component_func_type(types, id)?)
}
types::ComponentAnyTypeId::Resource(id) => {
TypeDef::Resource(self.resource_id(id.resource()))
}
})
}
fn convert_component(
&mut self,
types: types::TypesRef<'_>,
id: types::ComponentTypeId,
) -> Result<TypeComponentIndex> {
let ty = &types[id];
let mut result = TypeComponent::default();
for (name, ty) in ty.imports.iter() {
result.imports.insert(
name.clone(),
self.convert_component_entity_type(types, *ty)?,
);
}
for (name, ty) in ty.exports.iter() {
result.exports.insert(
name.clone(),
self.convert_component_entity_type(types, *ty)?,
);
}
Ok(self.component_types.components.push(result))
}
pub(crate) fn convert_instance(
&mut self,
types: types::TypesRef<'_>,
id: types::ComponentInstanceTypeId,
) -> Result<TypeComponentInstanceIndex> {
let ty = &types[id];
let mut result = TypeComponentInstance::default();
for (name, ty) in ty.exports.iter() {
result.exports.insert(
name.clone(),
self.convert_component_entity_type(types, *ty)?,
);
}
Ok(self.component_types.component_instances.push(result))
}
fn convert_module(
&mut self,
types: types::TypesRef<'_>,
id: types::ComponentCoreModuleTypeId,
) -> Result<TypeModuleIndex> {
let ty = &types[id];
let mut result = TypeModule::default();
for ((module, field), ty) in ty.imports.iter() {
result.imports.insert(
(module.clone(), field.clone()),
self.entity_type(types, ty)?,
);
}
for (name, ty) in ty.exports.iter() {
result
.exports
.insert(name.clone(), self.entity_type(types, ty)?);
}
Ok(self.component_types.modules.push(result))
}
fn entity_type(
&mut self,
types: types::TypesRef<'_>,
ty: &types::EntityType,
) -> Result<EntityType> {
Ok(match ty {
types::EntityType::Func(idx) => {
let ty = types[*idx].unwrap_func();
let ty = self.convert_func_type(ty);
EntityType::Function(self.module_types_builder_mut().wasm_func_type(*idx, ty))
}
types::EntityType::Table(ty) => EntityType::Table(self.convert_table_type(ty)),
types::EntityType::Memory(ty) => EntityType::Memory(ty.clone().into()),
types::EntityType::Global(ty) => EntityType::Global(self.convert_global_type(ty)),
types::EntityType::Tag(_) => bail!("exceptions proposal not implemented"),
})
}
fn defined_type(
&mut self,
types: types::TypesRef<'_>,
id: types::ComponentDefinedTypeId,
) -> Result<InterfaceType> {
let ret = match &types[id] {
types::ComponentDefinedType::Primitive(ty) => ty.into(),
types::ComponentDefinedType::Record(e) => {
InterfaceType::Record(self.record_type(types, e)?)
}
types::ComponentDefinedType::Variant(e) => {
InterfaceType::Variant(self.variant_type(types, e)?)
}
types::ComponentDefinedType::List(e) => InterfaceType::List(self.list_type(types, e)?),
types::ComponentDefinedType::Tuple(e) => {
InterfaceType::Tuple(self.tuple_type(types, e)?)
}
types::ComponentDefinedType::Flags(e) => InterfaceType::Flags(self.flags_type(e)),
types::ComponentDefinedType::Enum(e) => InterfaceType::Enum(self.enum_type(e)),
types::ComponentDefinedType::Option(e) => {
InterfaceType::Option(self.option_type(types, e)?)
}
types::ComponentDefinedType::Result { ok, err } => {
InterfaceType::Result(self.result_type(types, ok, err)?)
}
types::ComponentDefinedType::Own(r) => {
InterfaceType::Own(self.resource_id(r.resource()))
}
types::ComponentDefinedType::Borrow(r) => {
InterfaceType::Borrow(self.resource_id(r.resource()))
}
};
let info = self.type_information(&ret);
if info.depth > MAX_TYPE_DEPTH {
bail!("type nesting is too deep");
}
Ok(ret)
}
fn valtype(
&mut self,
types: types::TypesRef<'_>,
ty: &types::ComponentValType,
) -> Result<InterfaceType> {
match ty {
types::ComponentValType::Primitive(p) => Ok(p.into()),
types::ComponentValType::Type(id) => self.defined_type(types, *id),
}
}
fn record_type(
&mut self,
types: types::TypesRef<'_>,
ty: &types::RecordType,
) -> Result<TypeRecordIndex> {
let fields = ty
.fields
.iter()
.map(|(name, ty)| {
Ok(RecordField {
name: name.to_string(),
ty: self.valtype(types, ty)?,
})
})
.collect::<Result<Box<[_]>>>()?;
let abi = CanonicalAbiInfo::record(
fields
.iter()
.map(|field| self.component_types.canonical_abi(&field.ty)),
);
Ok(self.add_record_type(TypeRecord { fields, abi }))
}
fn variant_type(
&mut self,
types: types::TypesRef<'_>,
ty: &types::VariantType,
) -> Result<TypeVariantIndex> {
let cases = ty
.cases
.iter()
.map(|(name, case)| {
if case.refines.is_some() {
bail!("refines is not supported at this time");
}
Ok(VariantCase {
name: name.to_string(),
ty: match &case.ty.as_ref() {
Some(ty) => Some(self.valtype(types, ty)?),
None => None,
},
})
})
.collect::<Result<Box<[_]>>>()?;
let (info, abi) = VariantInfo::new(cases.iter().map(|c| {
c.ty.as_ref()
.map(|ty| self.component_types.canonical_abi(ty))
}));
Ok(self.add_variant_type(TypeVariant { cases, abi, info }))
}
fn tuple_type(
&mut self,
types: types::TypesRef<'_>,
ty: &types::TupleType,
) -> Result<TypeTupleIndex> {
let types = ty
.types
.iter()
.map(|ty| self.valtype(types, ty))
.collect::<Result<Box<[_]>>>()?;
Ok(self.new_tuple_type(types))
}
fn new_tuple_type(&mut self, types: Box<[InterfaceType]>) -> TypeTupleIndex {
let abi = CanonicalAbiInfo::record(
types
.iter()
.map(|ty| self.component_types.canonical_abi(ty)),
);
self.add_tuple_type(TypeTuple { types, abi })
}
fn flags_type(&mut self, flags: &IndexSet<KebabString>) -> TypeFlagsIndex {
let flags = TypeFlags {
names: flags.iter().map(|s| s.to_string()).collect(),
abi: CanonicalAbiInfo::flags(flags.len()),
};
self.add_flags_type(flags)
}
fn enum_type(&mut self, variants: &IndexSet<KebabString>) -> TypeEnumIndex {
let names = variants.iter().map(|s| s.to_string()).collect::<Box<[_]>>();
let (info, abi) = VariantInfo::new(names.iter().map(|_| None));
self.add_enum_type(TypeEnum { names, abi, info })
}
fn option_type(
&mut self,
types: types::TypesRef<'_>,
ty: &types::ComponentValType,
) -> Result<TypeOptionIndex> {
let ty = self.valtype(types, ty)?;
let (info, abi) = VariantInfo::new([None, Some(self.component_types.canonical_abi(&ty))]);
Ok(self.add_option_type(TypeOption { ty, abi, info }))
}
fn result_type(
&mut self,
types: types::TypesRef<'_>,
ok: &Option<types::ComponentValType>,
err: &Option<types::ComponentValType>,
) -> Result<TypeResultIndex> {
let ok = match ok {
Some(ty) => Some(self.valtype(types, ty)?),
None => None,
};
let err = match err {
Some(ty) => Some(self.valtype(types, ty)?),
None => None,
};
let (info, abi) = VariantInfo::new([
ok.as_ref().map(|t| self.component_types.canonical_abi(t)),
err.as_ref().map(|t| self.component_types.canonical_abi(t)),
]);
Ok(self.add_result_type(TypeResult { ok, err, abi, info }))
}
fn list_type(
&mut self,
types: types::TypesRef<'_>,
ty: &types::ComponentValType,
) -> Result<TypeListIndex> {
let element = self.valtype(types, ty)?;
Ok(self.add_list_type(TypeList { element }))
}
pub fn resource_id(&mut self, id: types::ResourceId) -> TypeResourceTableIndex {
self.resources.convert(id, &mut self.component_types)
}
pub fn add_func_type(&mut self, ty: TypeFunc) -> TypeFuncIndex {
intern(&mut self.functions, &mut self.component_types.functions, ty)
}
pub fn add_record_type(&mut self, ty: TypeRecord) -> TypeRecordIndex {
intern_and_fill_flat_types!(self, records, ty)
}
pub fn add_flags_type(&mut self, ty: TypeFlags) -> TypeFlagsIndex {
intern_and_fill_flat_types!(self, flags, ty)
}
pub fn add_tuple_type(&mut self, ty: TypeTuple) -> TypeTupleIndex {
intern_and_fill_flat_types!(self, tuples, ty)
}
pub fn add_variant_type(&mut self, ty: TypeVariant) -> TypeVariantIndex {
intern_and_fill_flat_types!(self, variants, ty)
}
pub fn add_enum_type(&mut self, ty: TypeEnum) -> TypeEnumIndex {
intern_and_fill_flat_types!(self, enums, ty)
}
pub fn add_option_type(&mut self, ty: TypeOption) -> TypeOptionIndex {
intern_and_fill_flat_types!(self, options, ty)
}
pub fn add_result_type(&mut self, ty: TypeResult) -> TypeResultIndex {
intern_and_fill_flat_types!(self, results, ty)
}
pub fn add_list_type(&mut self, ty: TypeList) -> TypeListIndex {
intern_and_fill_flat_types!(self, lists, ty)
}
pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo {
self.component_types.canonical_abi(ty)
}
pub fn flat_types(&self, ty: &InterfaceType) -> Option<FlatTypes<'_>> {
self.type_information(ty).flat.as_flat_types()
}
pub fn ty_contains_borrow_resource(&self, ty: &InterfaceType) -> bool {
self.type_information(ty).has_borrow
}
fn type_information(&self, ty: &InterfaceType) -> &TypeInformation {
match ty {
InterfaceType::U8
| InterfaceType::S8
| InterfaceType::Bool
| InterfaceType::U16
| InterfaceType::S16
| InterfaceType::U32
| InterfaceType::S32
| InterfaceType::Char
| InterfaceType::Own(_) => {
static INFO: TypeInformation = TypeInformation::primitive(FlatType::I32);
&INFO
}
InterfaceType::Borrow(_) => {
static INFO: TypeInformation = {
let mut info = TypeInformation::primitive(FlatType::I32);
info.has_borrow = true;
info
};
&INFO
}
InterfaceType::U64 | InterfaceType::S64 => {
static INFO: TypeInformation = TypeInformation::primitive(FlatType::I64);
&INFO
}
InterfaceType::Float32 => {
static INFO: TypeInformation = TypeInformation::primitive(FlatType::F32);
&INFO
}
InterfaceType::Float64 => {
static INFO: TypeInformation = TypeInformation::primitive(FlatType::F64);
&INFO
}
InterfaceType::String => {
static INFO: TypeInformation = TypeInformation::string();
&INFO
}
InterfaceType::List(i) => &self.type_info.lists[*i],
InterfaceType::Record(i) => &self.type_info.records[*i],
InterfaceType::Variant(i) => &self.type_info.variants[*i],
InterfaceType::Tuple(i) => &self.type_info.tuples[*i],
InterfaceType::Flags(i) => &self.type_info.flags[*i],
InterfaceType::Enum(i) => &self.type_info.enums[*i],
InterfaceType::Option(i) => &self.type_info.options[*i],
InterfaceType::Result(i) => &self.type_info.results[*i],
}
}
}
impl TypeConvert for ComponentTypesBuilder {
fn lookup_heap_type(&self, _index: wasmparser::UnpackedIndex) -> WasmHeapType {
panic!("heap types are not supported yet")
}
}
fn intern<T, U>(map: &mut HashMap<T, U>, list: &mut PrimaryMap<U, T>, item: T) -> U
where
T: Hash + Clone + Eq,
U: Copy + EntityRef,
{
if let Some(idx) = map.get(&item) {
return *idx;
}
let idx = list.push(item.clone());
map.insert(item, idx);
return idx;
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum TypeDef {
Component(TypeComponentIndex),
ComponentInstance(TypeComponentInstanceIndex),
ComponentFunc(TypeFuncIndex),
Interface(InterfaceType),
Module(TypeModuleIndex),
CoreFunc(ModuleInternedTypeIndex),
Resource(TypeResourceTableIndex),
}
#[derive(Serialize, Deserialize, Default)]
pub struct TypeModule {
pub imports: IndexMap<(String, String), EntityType>,
pub exports: IndexMap<String, EntityType>,
}
#[derive(Serialize, Deserialize, Default)]
pub struct TypeComponent {
pub imports: IndexMap<String, TypeDef>,
pub exports: IndexMap<String, TypeDef>,
}
#[derive(Serialize, Deserialize, Default)]
pub struct TypeComponentInstance {
pub exports: IndexMap<String, TypeDef>,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeFunc {
pub params: TypeTupleIndex,
pub results: TypeTupleIndex,
}
#[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum InterfaceType {
Bool,
S8,
U8,
S16,
U16,
S32,
U32,
S64,
U64,
Float32,
Float64,
Char,
String,
Record(TypeRecordIndex),
Variant(TypeVariantIndex),
List(TypeListIndex),
Tuple(TypeTupleIndex),
Flags(TypeFlagsIndex),
Enum(TypeEnumIndex),
Option(TypeOptionIndex),
Result(TypeResultIndex),
Own(TypeResourceTableIndex),
Borrow(TypeResourceTableIndex),
}
impl From<&wasmparser::PrimitiveValType> for InterfaceType {
fn from(ty: &wasmparser::PrimitiveValType) -> InterfaceType {
match ty {
wasmparser::PrimitiveValType::Bool => InterfaceType::Bool,
wasmparser::PrimitiveValType::S8 => InterfaceType::S8,
wasmparser::PrimitiveValType::U8 => InterfaceType::U8,
wasmparser::PrimitiveValType::S16 => InterfaceType::S16,
wasmparser::PrimitiveValType::U16 => InterfaceType::U16,
wasmparser::PrimitiveValType::S32 => InterfaceType::S32,
wasmparser::PrimitiveValType::U32 => InterfaceType::U32,
wasmparser::PrimitiveValType::S64 => InterfaceType::S64,
wasmparser::PrimitiveValType::U64 => InterfaceType::U64,
wasmparser::PrimitiveValType::Float32 => InterfaceType::Float32,
wasmparser::PrimitiveValType::Float64 => InterfaceType::Float64,
wasmparser::PrimitiveValType::Char => InterfaceType::Char,
wasmparser::PrimitiveValType::String => InterfaceType::String,
}
}
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct CanonicalAbiInfo {
pub size32: u32,
pub align32: u32,
pub size64: u32,
pub align64: u32,
pub flat_count: Option<u8>,
}
impl Default for CanonicalAbiInfo {
fn default() -> CanonicalAbiInfo {
CanonicalAbiInfo {
size32: 0,
align32: 1,
size64: 0,
align64: 1,
flat_count: Some(0),
}
}
}
const fn align_to(a: u32, b: u32) -> u32 {
assert!(b.is_power_of_two());
(a + (b - 1)) & !(b - 1)
}
const fn max(a: u32, b: u32) -> u32 {
if a > b {
a
} else {
b
}
}
impl CanonicalAbiInfo {
const ZERO: CanonicalAbiInfo = CanonicalAbiInfo {
size32: 0,
align32: 1,
size64: 0,
align64: 1,
flat_count: Some(0),
};
pub const SCALAR1: CanonicalAbiInfo = CanonicalAbiInfo::scalar(1);
pub const SCALAR2: CanonicalAbiInfo = CanonicalAbiInfo::scalar(2);
pub const SCALAR4: CanonicalAbiInfo = CanonicalAbiInfo::scalar(4);
pub const SCALAR8: CanonicalAbiInfo = CanonicalAbiInfo::scalar(8);
const fn scalar(size: u32) -> CanonicalAbiInfo {
CanonicalAbiInfo {
size32: size,
align32: size,
size64: size,
align64: size,
flat_count: Some(1),
}
}
pub const POINTER_PAIR: CanonicalAbiInfo = CanonicalAbiInfo {
size32: 8,
align32: 4,
size64: 16,
align64: 8,
flat_count: Some(2),
};
pub fn record<'a>(fields: impl Iterator<Item = &'a CanonicalAbiInfo>) -> CanonicalAbiInfo {
let mut ret = CanonicalAbiInfo::default();
for field in fields {
ret.size32 = align_to(ret.size32, field.align32) + field.size32;
ret.align32 = ret.align32.max(field.align32);
ret.size64 = align_to(ret.size64, field.align64) + field.size64;
ret.align64 = ret.align64.max(field.align64);
ret.flat_count = add_flat(ret.flat_count, field.flat_count);
}
ret.size32 = align_to(ret.size32, ret.align32);
ret.size64 = align_to(ret.size64, ret.align64);
return ret;
}
pub const fn record_static(fields: &[CanonicalAbiInfo]) -> CanonicalAbiInfo {
let mut ret = CanonicalAbiInfo::ZERO;
let mut i = 0;
while i < fields.len() {
let field = &fields[i];
ret.size32 = align_to(ret.size32, field.align32) + field.size32;
ret.align32 = max(ret.align32, field.align32);
ret.size64 = align_to(ret.size64, field.align64) + field.size64;
ret.align64 = max(ret.align64, field.align64);
ret.flat_count = add_flat(ret.flat_count, field.flat_count);
i += 1;
}
ret.size32 = align_to(ret.size32, ret.align32);
ret.size64 = align_to(ret.size64, ret.align64);
return ret;
}
pub fn next_field32(&self, offset: &mut u32) -> u32 {
*offset = align_to(*offset, self.align32) + self.size32;
*offset - self.size32
}
pub fn next_field32_size(&self, offset: &mut usize) -> usize {
let cur = u32::try_from(*offset).unwrap();
let cur = align_to(cur, self.align32) + self.size32;
*offset = usize::try_from(cur).unwrap();
usize::try_from(cur - self.size32).unwrap()
}
pub fn next_field64(&self, offset: &mut u32) -> u32 {
*offset = align_to(*offset, self.align64) + self.size64;
*offset - self.size64
}
pub fn next_field64_size(&self, offset: &mut usize) -> usize {
let cur = u32::try_from(*offset).unwrap();
let cur = align_to(cur, self.align64) + self.size64;
*offset = usize::try_from(cur).unwrap();
usize::try_from(cur - self.size64).unwrap()
}
pub const fn flags(count: usize) -> CanonicalAbiInfo {
let (size, align, flat_count) = match FlagsSize::from_count(count) {
FlagsSize::Size0 => (0, 1, 0),
FlagsSize::Size1 => (1, 1, 1),
FlagsSize::Size2 => (2, 2, 1),
FlagsSize::Size4Plus(n) => ((n as u32) * 4, 4, n),
};
CanonicalAbiInfo {
size32: size,
align32: align,
size64: size,
align64: align,
flat_count: Some(flat_count),
}
}
fn variant<'a, I>(cases: I) -> CanonicalAbiInfo
where
I: IntoIterator<Item = Option<&'a CanonicalAbiInfo>>,
I::IntoIter: ExactSizeIterator,
{
let cases = cases.into_iter();
let discrim_size = u32::from(DiscriminantSize::from_count(cases.len()).unwrap());
let mut max_size32 = 0;
let mut max_align32 = discrim_size;
let mut max_size64 = 0;
let mut max_align64 = discrim_size;
let mut max_case_count = Some(0);
for case in cases {
if let Some(case) = case {
max_size32 = max_size32.max(case.size32);
max_align32 = max_align32.max(case.align32);
max_size64 = max_size64.max(case.size64);
max_align64 = max_align64.max(case.align64);
max_case_count = max_flat(max_case_count, case.flat_count);
}
}
CanonicalAbiInfo {
size32: align_to(
align_to(discrim_size, max_align32) + max_size32,
max_align32,
),
align32: max_align32,
size64: align_to(
align_to(discrim_size, max_align64) + max_size64,
max_align64,
),
align64: max_align64,
flat_count: add_flat(max_case_count, Some(1)),
}
}
pub const fn variant_static(cases: &[Option<CanonicalAbiInfo>]) -> CanonicalAbiInfo {
let discrim_size = match DiscriminantSize::from_count(cases.len()) {
Some(size) => size.byte_size(),
None => unreachable!(),
};
let mut max_size32 = 0;
let mut max_align32 = discrim_size;
let mut max_size64 = 0;
let mut max_align64 = discrim_size;
let mut max_case_count = Some(0);
let mut i = 0;
while i < cases.len() {
let case = &cases[i];
if let Some(case) = case {
max_size32 = max(max_size32, case.size32);
max_align32 = max(max_align32, case.align32);
max_size64 = max(max_size64, case.size64);
max_align64 = max(max_align64, case.align64);
max_case_count = max_flat(max_case_count, case.flat_count);
}
i += 1;
}
CanonicalAbiInfo {
size32: align_to(
align_to(discrim_size, max_align32) + max_size32,
max_align32,
),
align32: max_align32,
size64: align_to(
align_to(discrim_size, max_align64) + max_size64,
max_align64,
),
align64: max_align64,
flat_count: add_flat(max_case_count, Some(1)),
}
}
pub fn flat_count(&self, max: usize) -> Option<usize> {
let flat = usize::from(self.flat_count?);
if flat > max {
None
} else {
Some(flat)
}
}
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct VariantInfo {
#[serde(with = "serde_discrim_size")]
pub size: DiscriminantSize,
pub payload_offset32: u32,
pub payload_offset64: u32,
}
impl VariantInfo {
pub fn new<'a, I>(cases: I) -> (VariantInfo, CanonicalAbiInfo)
where
I: IntoIterator<Item = Option<&'a CanonicalAbiInfo>>,
I::IntoIter: ExactSizeIterator,
{
let cases = cases.into_iter();
let size = DiscriminantSize::from_count(cases.len()).unwrap();
let abi = CanonicalAbiInfo::variant(cases);
(
VariantInfo {
size,
payload_offset32: align_to(u32::from(size), abi.align32),
payload_offset64: align_to(u32::from(size), abi.align64),
},
abi,
)
}
pub const fn new_static(cases: &[Option<CanonicalAbiInfo>]) -> VariantInfo {
let size = match DiscriminantSize::from_count(cases.len()) {
Some(size) => size,
None => unreachable!(),
};
let abi = CanonicalAbiInfo::variant_static(cases);
VariantInfo {
size,
payload_offset32: align_to(size.byte_size(), abi.align32),
payload_offset64: align_to(size.byte_size(), abi.align64),
}
}
}
mod serde_discrim_size {
use super::DiscriminantSize;
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(disc: &DiscriminantSize, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
u32::from(*disc).serialize(ser)
}
pub fn deserialize<'de, D>(deser: D) -> Result<DiscriminantSize, D::Error>
where
D: Deserializer<'de>,
{
match u32::deserialize(deser)? {
1 => Ok(DiscriminantSize::Size1),
2 => Ok(DiscriminantSize::Size2),
4 => Ok(DiscriminantSize::Size4),
_ => Err(D::Error::custom("invalid discriminant size")),
}
}
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeRecord {
pub fields: Box<[RecordField]>,
pub abi: CanonicalAbiInfo,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct RecordField {
pub name: String,
pub ty: InterfaceType,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeVariant {
pub cases: Box<[VariantCase]>,
pub abi: CanonicalAbiInfo,
pub info: VariantInfo,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct VariantCase {
pub name: String,
pub ty: Option<InterfaceType>,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeTuple {
pub types: Box<[InterfaceType]>,
pub abi: CanonicalAbiInfo,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeFlags {
pub names: Box<[String]>,
pub abi: CanonicalAbiInfo,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeEnum {
pub names: Box<[String]>,
pub abi: CanonicalAbiInfo,
pub info: VariantInfo,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeOption {
pub ty: InterfaceType,
pub abi: CanonicalAbiInfo,
pub info: VariantInfo,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeResult {
pub ok: Option<InterfaceType>,
pub err: Option<InterfaceType>,
pub abi: CanonicalAbiInfo,
pub info: VariantInfo,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeResourceTable {
pub ty: ResourceIndex,
pub instance: RuntimeComponentInstanceIndex,
}
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TypeList {
pub element: InterfaceType,
}
const MAX_FLAT_TYPES: usize = if MAX_FLAT_PARAMS > MAX_FLAT_RESULTS {
MAX_FLAT_PARAMS
} else {
MAX_FLAT_RESULTS
};
const fn add_flat(a: Option<u8>, b: Option<u8>) -> Option<u8> {
const MAX: u8 = MAX_FLAT_TYPES as u8;
let sum = match (a, b) {
(Some(a), Some(b)) => match a.checked_add(b) {
Some(c) => c,
None => return None,
},
_ => return None,
};
if sum > MAX {
None
} else {
Some(sum)
}
}
const fn max_flat(a: Option<u8>, b: Option<u8>) -> Option<u8> {
match (a, b) {
(Some(a), Some(b)) => {
if a > b {
Some(a)
} else {
Some(b)
}
}
_ => None,
}
}
pub struct FlatTypes<'a> {
pub memory32: &'a [FlatType],
pub memory64: &'a [FlatType],
}
#[allow(missing_docs)]
impl FlatTypes<'_> {
pub fn len(&self) -> usize {
assert_eq!(self.memory32.len(), self.memory64.len());
self.memory32.len()
}
}
#[derive(PartialEq, Eq, Copy, Clone)]
#[allow(missing_docs)]
pub enum FlatType {
I32,
I64,
F32,
F64,
}
struct FlatTypesStorage {
memory32: [FlatType; MAX_FLAT_TYPES],
memory64: [FlatType; MAX_FLAT_TYPES],
len: u8,
}
impl FlatTypesStorage {
const fn new() -> FlatTypesStorage {
FlatTypesStorage {
memory32: [FlatType::I32; MAX_FLAT_TYPES],
memory64: [FlatType::I32; MAX_FLAT_TYPES],
len: 0,
}
}
fn as_flat_types(&self) -> Option<FlatTypes<'_>> {
let len = usize::from(self.len);
if len > MAX_FLAT_TYPES {
assert_eq!(len, MAX_FLAT_TYPES + 1);
None
} else {
Some(FlatTypes {
memory32: &self.memory32[..len],
memory64: &self.memory64[..len],
})
}
}
fn push(&mut self, t32: FlatType, t64: FlatType) -> bool {
let len = usize::from(self.len);
if len < MAX_FLAT_TYPES {
self.memory32[len] = t32;
self.memory64[len] = t64;
self.len += 1;
true
} else {
if len == MAX_FLAT_TYPES {
self.len += 1;
}
false
}
}
}
impl FlatType {
fn join(&mut self, other: FlatType) {
if *self == other {
return;
}
*self = match (*self, other) {
(FlatType::I32, FlatType::F32) | (FlatType::F32, FlatType::I32) => FlatType::I32,
_ => FlatType::I64,
};
}
}
#[derive(Default)]
struct TypeInformationCache {
records: PrimaryMap<TypeRecordIndex, TypeInformation>,
variants: PrimaryMap<TypeVariantIndex, TypeInformation>,
tuples: PrimaryMap<TypeTupleIndex, TypeInformation>,
enums: PrimaryMap<TypeEnumIndex, TypeInformation>,
flags: PrimaryMap<TypeFlagsIndex, TypeInformation>,
options: PrimaryMap<TypeOptionIndex, TypeInformation>,
results: PrimaryMap<TypeResultIndex, TypeInformation>,
lists: PrimaryMap<TypeListIndex, TypeInformation>,
}
struct TypeInformation {
depth: u32,
flat: FlatTypesStorage,
has_borrow: bool,
}
impl TypeInformation {
const fn new() -> TypeInformation {
TypeInformation {
depth: 0,
flat: FlatTypesStorage::new(),
has_borrow: false,
}
}
const fn primitive(flat: FlatType) -> TypeInformation {
let mut info = TypeInformation::new();
info.depth = 1;
info.flat.memory32[0] = flat;
info.flat.memory64[0] = flat;
info.flat.len = 1;
info
}
const fn string() -> TypeInformation {
let mut info = TypeInformation::new();
info.depth = 1;
info.flat.memory32[0] = FlatType::I32;
info.flat.memory32[1] = FlatType::I32;
info.flat.memory64[0] = FlatType::I64;
info.flat.memory64[1] = FlatType::I64;
info.flat.len = 2;
info
}
fn build_record<'a>(&mut self, types: impl Iterator<Item = &'a TypeInformation>) {
self.depth = 1;
for info in types {
self.depth = self.depth.max(1 + info.depth);
self.has_borrow = self.has_borrow || info.has_borrow;
match info.flat.as_flat_types() {
Some(types) => {
for (t32, t64) in types.memory32.iter().zip(types.memory64) {
if !self.flat.push(*t32, *t64) {
break;
}
}
}
None => {
self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
}
}
}
}
fn build_variant<'a, I>(&mut self, cases: I)
where
I: IntoIterator<Item = Option<&'a TypeInformation>>,
{
let cases = cases.into_iter();
self.flat.push(FlatType::I32, FlatType::I32);
self.depth = 1;
for info in cases {
let info = match info {
Some(info) => info,
None => continue,
};
self.depth = self.depth.max(1 + info.depth);
self.has_borrow = self.has_borrow || info.has_borrow;
if usize::from(self.flat.len) > MAX_FLAT_TYPES {
continue;
}
let types = match info.flat.as_flat_types() {
Some(types) => types,
None => {
self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
continue;
}
};
if types.memory32.len() >= MAX_FLAT_TYPES {
self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
continue;
}
let dst = self
.flat
.memory32
.iter_mut()
.zip(&mut self.flat.memory64)
.skip(1);
for (i, ((t32, t64), (dst32, dst64))) in types
.memory32
.iter()
.zip(types.memory64)
.zip(dst)
.enumerate()
{
if i + 1 < usize::from(self.flat.len) {
dst32.join(*t32);
dst64.join(*t64);
} else {
self.flat.len += 1;
*dst32 = *t32;
*dst64 = *t64;
}
}
}
}
fn records(&mut self, types: &ComponentTypesBuilder, ty: &TypeRecord) {
self.build_record(ty.fields.iter().map(|f| types.type_information(&f.ty)));
}
fn tuples(&mut self, types: &ComponentTypesBuilder, ty: &TypeTuple) {
self.build_record(ty.types.iter().map(|t| types.type_information(t)));
}
fn enums(&mut self, _types: &ComponentTypesBuilder, _ty: &TypeEnum) {
self.depth = 1;
self.flat.push(FlatType::I32, FlatType::I32);
}
fn flags(&mut self, _types: &ComponentTypesBuilder, ty: &TypeFlags) {
self.depth = 1;
match FlagsSize::from_count(ty.names.len()) {
FlagsSize::Size0 => {}
FlagsSize::Size1 | FlagsSize::Size2 => {
self.flat.push(FlatType::I32, FlatType::I32);
}
FlagsSize::Size4Plus(n) => {
for _ in 0..n {
self.flat.push(FlatType::I32, FlatType::I32);
}
}
}
}
fn variants(&mut self, types: &ComponentTypesBuilder, ty: &TypeVariant) {
self.build_variant(
ty.cases
.iter()
.map(|c| c.ty.as_ref().map(|ty| types.type_information(ty))),
)
}
fn results(&mut self, types: &ComponentTypesBuilder, ty: &TypeResult) {
self.build_variant([
ty.ok.as_ref().map(|ty| types.type_information(ty)),
ty.err.as_ref().map(|ty| types.type_information(ty)),
])
}
fn options(&mut self, types: &ComponentTypesBuilder, ty: &TypeOption) {
self.build_variant([None, Some(types.type_information(&ty.ty))]);
}
fn lists(&mut self, types: &ComponentTypesBuilder, ty: &TypeList) {
*self = TypeInformation::string();
let info = types.type_information(&ty.element);
self.depth += info.depth;
self.has_borrow = info.has_borrow;
}
}