use super::{
component::{ComponentState, ExternKind},
core::Module,
};
use crate::validator::names::KebabString;
use crate::{
ArrayType, BinaryReaderError, Export, ExternalKind, FuncType, GlobalType, Import, MemoryType,
PrimitiveValType, RefType, Result, StructType, StructuralType, SubType, TableType, TypeRef,
ValType,
};
use indexmap::{IndexMap, IndexSet};
use std::collections::HashMap;
use std::collections::HashSet;
use std::ops::Index;
use std::sync::atomic::{AtomicU64, Ordering};
use std::{
borrow::Borrow,
hash::{Hash, Hasher},
mem,
ops::{Deref, DerefMut},
sync::Arc,
};
const MAX_FLAT_FUNC_PARAMS: usize = 16;
const MAX_FLAT_FUNC_RESULTS: usize = 1;
const MAX_LOWERED_TYPES: usize = MAX_FLAT_FUNC_PARAMS + 1;
pub(crate) struct LoweredTypes {
types: [ValType; MAX_LOWERED_TYPES],
len: usize,
max: usize,
}
impl LoweredTypes {
fn new(max: usize) -> Self {
assert!(max <= MAX_LOWERED_TYPES);
Self {
types: [ValType::I32; MAX_LOWERED_TYPES],
len: 0,
max,
}
}
fn len(&self) -> usize {
self.len
}
fn maxed(&self) -> bool {
self.len == self.max
}
fn get_mut(&mut self, index: usize) -> Option<&mut ValType> {
if index < self.len {
Some(&mut self.types[index])
} else {
None
}
}
fn push(&mut self, ty: ValType) -> bool {
if self.maxed() {
return false;
}
self.types[self.len] = ty;
self.len += 1;
true
}
fn clear(&mut self) {
self.len = 0;
}
pub fn as_slice(&self) -> &[ValType] {
&self.types[..self.len]
}
pub fn iter(&self) -> impl Iterator<Item = ValType> + '_ {
self.as_slice().iter().copied()
}
}
pub(crate) struct LoweringInfo {
pub(crate) params: LoweredTypes,
pub(crate) results: LoweredTypes,
pub(crate) requires_memory: bool,
pub(crate) requires_realloc: bool,
}
impl LoweringInfo {
pub(crate) fn into_func_type(self) -> FuncType {
FuncType::new(
self.params.as_slice().iter().copied(),
self.results.as_slice().iter().copied(),
)
}
}
impl Default for LoweringInfo {
fn default() -> Self {
Self {
params: LoweredTypes::new(MAX_FLAT_FUNC_PARAMS),
results: LoweredTypes::new(MAX_FLAT_FUNC_RESULTS),
requires_memory: false,
requires_realloc: false,
}
}
}
fn push_primitive_wasm_types(ty: &PrimitiveValType, lowered_types: &mut LoweredTypes) -> bool {
match ty {
PrimitiveValType::Bool
| PrimitiveValType::S8
| PrimitiveValType::U8
| PrimitiveValType::S16
| PrimitiveValType::U16
| PrimitiveValType::S32
| PrimitiveValType::U32
| PrimitiveValType::Char => lowered_types.push(ValType::I32),
PrimitiveValType::S64 | PrimitiveValType::U64 => lowered_types.push(ValType::I64),
PrimitiveValType::Float32 => lowered_types.push(ValType::F32),
PrimitiveValType::Float64 => lowered_types.push(ValType::F64),
PrimitiveValType::String => {
lowered_types.push(ValType::I32) && lowered_types.push(ValType::I32)
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(C)] pub struct TypeId {
pub(crate) index: usize,
pub(crate) info: TypeInfo,
unique_id: u32,
}
const _: () = {
assert!(std::mem::size_of::<TypeId>() <= 16);
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct TypeInfo(u32);
impl TypeInfo {
pub(crate) fn new() -> TypeInfo {
TypeInfo::_new(1, false)
}
pub(crate) fn borrow() -> TypeInfo {
TypeInfo::_new(1, true)
}
pub(crate) fn core(size: u32) -> TypeInfo {
TypeInfo::_new(size, false)
}
fn _new(size: u32, contains_borrow: bool) -> TypeInfo {
assert!(size < (1 << 24));
TypeInfo(size | ((contains_borrow as u32) << 31))
}
pub(crate) fn combine(&mut self, other: TypeInfo, offset: usize) -> Result<()> {
*self = TypeInfo::_new(
super::combine_type_sizes(self.size(), other.size(), offset)?,
self.contains_borrow() || other.contains_borrow(),
);
Ok(())
}
pub(crate) fn size(&self) -> u32 {
self.0 & 0xffffff
}
pub(crate) fn contains_borrow(&self) -> bool {
(self.0 >> 31) != 0
}
}
#[derive(Debug)]
pub enum Type {
Sub(SubType),
Module(Box<ModuleType>),
Instance(Box<InstanceType>),
Component(Box<ComponentType>),
ComponentInstance(Box<ComponentInstanceType>),
ComponentFunc(ComponentFuncType),
Defined(ComponentDefinedType),
Resource(ResourceId),
}
impl Type {
pub fn unwrap_func(&self) -> &FuncType {
match self {
Type::Sub(SubType {
structural_type: StructuralType::Func(ft),
..
}) => ft,
_ => panic!("not a function type"),
}
}
pub fn unwrap_array(&self) -> &ArrayType {
match self {
Self::Sub(SubType {
structural_type: StructuralType::Array(ty),
..
}) => ty,
_ => panic!("not an array type"),
}
}
pub fn unwrap_struct(&self) -> &StructType {
match self {
Self::Sub(SubType {
structural_type: StructuralType::Struct(ty),
..
}) => ty,
_ => panic!("not a struct type"),
}
}
pub fn unwrap_module(&self) -> &ModuleType {
match self {
Self::Module(ty) => ty,
_ => panic!("not a module type"),
}
}
pub fn unwrap_instance(&self) -> &InstanceType {
match self {
Self::Instance(ty) => ty,
_ => panic!("not an instance type"),
}
}
pub fn unwrap_component(&self) -> &ComponentType {
match self {
Self::Component(ty) => ty,
_ => panic!("not a component type"),
}
}
pub fn unwrap_component_instance(&self) -> &ComponentInstanceType {
match self {
Self::ComponentInstance(ty) => ty,
_ => panic!("not a component instance type"),
}
}
pub fn unwrap_component_func(&self) -> &ComponentFuncType {
match self {
Self::ComponentFunc(ty) => ty,
_ => panic!("not a component function type"),
}
}
pub fn unwrap_defined(&self) -> &ComponentDefinedType {
match self {
Self::Defined(ty) => ty,
_ => panic!("not a defined type"),
}
}
pub fn unwrap_resource(&self) -> ResourceId {
match self {
Self::Resource(id) => *id,
_ => panic!("not a resource type"),
}
}
pub(crate) fn info(&self) -> TypeInfo {
match self {
Self::Sub(ty) => {
let size = 1 + match ty.clone().structural_type {
StructuralType::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32,
StructuralType::Array(_) => 2,
StructuralType::Struct(ty) => 1 + 2 * ty.fields.len() as u32,
};
TypeInfo::core(size)
}
Self::Module(ty) => ty.info,
Self::Instance(ty) => ty.info,
Self::Component(ty) => ty.info,
Self::ComponentInstance(ty) => ty.info,
Self::ComponentFunc(ty) => ty.info,
Self::Defined(ty) => ty.info(),
Self::Resource(_) => TypeInfo::new(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ComponentValType {
Primitive(PrimitiveValType),
Type(TypeId),
}
impl ComponentValType {
pub(crate) fn contains_ptr(&self, types: &TypeList) -> bool {
match self {
ComponentValType::Primitive(ty) => ty.contains_ptr(),
ComponentValType::Type(ty) => types[*ty].unwrap_defined().contains_ptr(types),
}
}
fn push_wasm_types(&self, types: &TypeList, lowered_types: &mut LoweredTypes) -> bool {
match self {
Self::Primitive(ty) => push_primitive_wasm_types(ty, lowered_types),
Self::Type(id) => types[*id]
.unwrap_defined()
.push_wasm_types(types, lowered_types),
}
}
pub(crate) fn info(&self) -> TypeInfo {
match self {
Self::Primitive(_) => TypeInfo::new(),
Self::Type(id) => id.info,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum EntityType {
Func(TypeId),
Table(TableType),
Memory(MemoryType),
Global(GlobalType),
Tag(TypeId),
}
impl EntityType {
pub(crate) fn desc(&self) -> &'static str {
match self {
Self::Func(_) => "func",
Self::Table(_) => "table",
Self::Memory(_) => "memory",
Self::Global(_) => "global",
Self::Tag(_) => "tag",
}
}
pub(crate) fn info(&self) -> TypeInfo {
match self {
Self::Func(id) | Self::Tag(id) => id.info,
Self::Table(_) | Self::Memory(_) | Self::Global(_) => TypeInfo::new(),
}
}
}
trait ModuleImportKey {
fn module(&self) -> &str;
fn name(&self) -> &str;
}
impl<'a> Borrow<dyn ModuleImportKey + 'a> for (String, String) {
fn borrow(&self) -> &(dyn ModuleImportKey + 'a) {
self
}
}
impl Hash for (dyn ModuleImportKey + '_) {
fn hash<H: Hasher>(&self, state: &mut H) {
self.module().hash(state);
self.name().hash(state);
}
}
impl PartialEq for (dyn ModuleImportKey + '_) {
fn eq(&self, other: &Self) -> bool {
self.module() == other.module() && self.name() == other.name()
}
}
impl Eq for (dyn ModuleImportKey + '_) {}
impl ModuleImportKey for (String, String) {
fn module(&self) -> &str {
&self.0
}
fn name(&self) -> &str {
&self.1
}
}
impl ModuleImportKey for (&str, &str) {
fn module(&self) -> &str {
self.0
}
fn name(&self) -> &str {
self.1
}
}
#[derive(Debug, Clone)]
pub struct ModuleType {
pub(crate) info: TypeInfo,
pub imports: IndexMap<(String, String), EntityType>,
pub exports: IndexMap<String, EntityType>,
}
impl ModuleType {
pub fn lookup_import(&self, module: &str, name: &str) -> Option<&EntityType> {
self.imports.get(&(module, name) as &dyn ModuleImportKey)
}
}
#[derive(Debug, Clone)]
pub enum InstanceTypeKind {
Instantiated(TypeId),
Exports(IndexMap<String, EntityType>),
}
#[derive(Debug, Clone)]
pub struct InstanceType {
pub(crate) info: TypeInfo,
pub kind: InstanceTypeKind,
}
impl InstanceType {
pub fn exports<'a>(&'a self, types: TypesRef<'a>) -> &'a IndexMap<String, EntityType> {
self.internal_exports(types.list)
}
pub(crate) fn internal_exports<'a>(
&'a self,
types: &'a TypeList,
) -> &'a IndexMap<String, EntityType> {
match &self.kind {
InstanceTypeKind::Instantiated(id) => &types[*id].unwrap_module().exports,
InstanceTypeKind::Exports(exports) => exports,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ComponentEntityType {
Module(TypeId),
Func(TypeId),
Value(ComponentValType),
Type {
referenced: TypeId,
created: TypeId,
},
Instance(TypeId),
Component(TypeId),
}
impl ComponentEntityType {
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
SubtypeCx::new(at.list, bt.list)
.component_entity_type(a, b, 0)
.is_ok()
}
pub(crate) fn desc(&self) -> &'static str {
match self {
Self::Module(_) => "module",
Self::Func(_) => "func",
Self::Value(_) => "value",
Self::Type { .. } => "type",
Self::Instance(_) => "instance",
Self::Component(_) => "component",
}
}
pub(crate) fn info(&self) -> TypeInfo {
match self {
Self::Module(ty)
| Self::Func(ty)
| Self::Type { referenced: ty, .. }
| Self::Instance(ty)
| Self::Component(ty) => ty.info,
Self::Value(ty) => ty.info(),
}
}
}
#[derive(Debug, Clone)]
pub struct ComponentType {
pub(crate) info: TypeInfo,
pub imports: IndexMap<String, ComponentEntityType>,
pub exports: IndexMap<String, ComponentEntityType>,
pub imported_resources: Vec<(ResourceId, Vec<usize>)>,
pub defined_resources: Vec<(ResourceId, Vec<usize>)>,
pub explicit_resources: IndexMap<ResourceId, Vec<usize>>,
}
#[derive(Debug, Clone)]
pub struct ComponentInstanceType {
pub(crate) info: TypeInfo,
pub exports: IndexMap<String, ComponentEntityType>,
pub defined_resources: Vec<ResourceId>,
pub explicit_resources: IndexMap<ResourceId, Vec<usize>>,
}
#[derive(Debug, Clone)]
pub struct ComponentFuncType {
pub(crate) info: TypeInfo,
pub params: Box<[(KebabString, ComponentValType)]>,
pub results: Box<[(Option<KebabString>, ComponentValType)]>,
}
impl ComponentFuncType {
pub(crate) fn lower(&self, types: &TypeList, is_lower: bool) -> LoweringInfo {
let mut info = LoweringInfo::default();
for (_, ty) in self.params.iter() {
if is_lower {
if !info.requires_memory {
info.requires_memory = ty.contains_ptr(types);
}
} else {
if !info.requires_realloc {
info.requires_realloc = ty.contains_ptr(types);
}
}
if !ty.push_wasm_types(types, &mut info.params) {
info.params.clear();
assert!(info.params.push(ValType::I32));
info.requires_memory = true;
if !is_lower {
info.requires_realloc = true;
}
break;
}
}
for (_, ty) in self.results.iter() {
if is_lower && !info.requires_realloc {
info.requires_realloc = ty.contains_ptr(types);
}
if !ty.push_wasm_types(types, &mut info.results) {
info.results.clear();
if is_lower {
info.params.max = MAX_LOWERED_TYPES;
assert!(info.params.push(ValType::I32));
} else {
assert!(info.results.push(ValType::I32));
}
info.requires_memory = true;
break;
}
}
info.requires_memory |= info.requires_realloc;
info
}
}
#[derive(Debug, Clone)]
pub struct VariantCase {
pub ty: Option<ComponentValType>,
pub refines: Option<KebabString>,
}
#[derive(Debug, Clone)]
pub struct RecordType {
pub(crate) info: TypeInfo,
pub fields: IndexMap<KebabString, ComponentValType>,
}
#[derive(Debug, Clone)]
pub struct VariantType {
pub(crate) info: TypeInfo,
pub cases: IndexMap<KebabString, VariantCase>,
}
#[derive(Debug, Clone)]
pub struct TupleType {
pub(crate) info: TypeInfo,
pub types: Box<[ComponentValType]>,
}
#[derive(Debug, Clone)]
pub enum ComponentDefinedType {
Primitive(PrimitiveValType),
Record(RecordType),
Variant(VariantType),
List(ComponentValType),
Tuple(TupleType),
Flags(IndexSet<KebabString>),
Enum(IndexSet<KebabString>),
Option(ComponentValType),
Result {
ok: Option<ComponentValType>,
err: Option<ComponentValType>,
},
Own(TypeId),
Borrow(TypeId),
}
impl ComponentDefinedType {
pub(crate) fn contains_ptr(&self, types: &TypeList) -> bool {
match self {
Self::Primitive(ty) => ty.contains_ptr(),
Self::Record(r) => r.fields.values().any(|ty| ty.contains_ptr(types)),
Self::Variant(v) => v
.cases
.values()
.any(|case| case.ty.map(|ty| ty.contains_ptr(types)).unwrap_or(false)),
Self::List(_) => true,
Self::Tuple(t) => t.types.iter().any(|ty| ty.contains_ptr(types)),
Self::Flags(_) | Self::Enum(_) | Self::Own(_) | Self::Borrow(_) => false,
Self::Option(ty) => ty.contains_ptr(types),
Self::Result { ok, err } => {
ok.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
|| err.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
}
}
}
pub(crate) fn info(&self) -> TypeInfo {
match self {
Self::Primitive(_) | Self::Flags(_) | Self::Enum(_) | Self::Own(_) => TypeInfo::new(),
Self::Borrow(_) => TypeInfo::borrow(),
Self::Record(r) => r.info,
Self::Variant(v) => v.info,
Self::Tuple(t) => t.info,
Self::List(ty) | Self::Option(ty) => ty.info(),
Self::Result { ok, err } => {
let default = TypeInfo::new();
let mut info = ok.map(|ty| ty.info()).unwrap_or(default);
info.combine(err.map(|ty| ty.info()).unwrap_or(default), 0)
.unwrap();
info
}
}
}
fn push_wasm_types(&self, types: &TypeList, lowered_types: &mut LoweredTypes) -> bool {
match self {
Self::Primitive(ty) => push_primitive_wasm_types(ty, lowered_types),
Self::Record(r) => r
.fields
.iter()
.all(|(_, ty)| ty.push_wasm_types(types, lowered_types)),
Self::Variant(v) => Self::push_variant_wasm_types(
v.cases.iter().filter_map(|(_, case)| case.ty.as_ref()),
types,
lowered_types,
),
Self::List(_) => lowered_types.push(ValType::I32) && lowered_types.push(ValType::I32),
Self::Tuple(t) => t
.types
.iter()
.all(|ty| ty.push_wasm_types(types, lowered_types)),
Self::Flags(names) => {
(0..(names.len() + 31) / 32).all(|_| lowered_types.push(ValType::I32))
}
Self::Enum(_) | Self::Own(_) | Self::Borrow(_) => lowered_types.push(ValType::I32),
Self::Option(ty) => {
Self::push_variant_wasm_types([ty].into_iter(), types, lowered_types)
}
Self::Result { ok, err } => {
Self::push_variant_wasm_types(ok.iter().chain(err.iter()), types, lowered_types)
}
}
}
fn push_variant_wasm_types<'a>(
cases: impl Iterator<Item = &'a ComponentValType>,
types: &TypeList,
lowered_types: &mut LoweredTypes,
) -> bool {
if !lowered_types.push(ValType::I32) {
return false;
}
let start = lowered_types.len();
for ty in cases {
let mut temp = LoweredTypes::new(lowered_types.max);
if !ty.push_wasm_types(types, &mut temp) {
return false;
}
for (i, ty) in temp.iter().enumerate() {
match lowered_types.get_mut(start + i) {
Some(prev) => *prev = Self::join_types(*prev, ty),
None => {
if !lowered_types.push(ty) {
return false;
}
}
}
}
}
true
}
fn join_types(a: ValType, b: ValType) -> ValType {
use ValType::*;
match (a, b) {
(I32, I32) | (I64, I64) | (F32, F32) | (F64, F64) => a,
(I32, F32) | (F32, I32) => I32,
(_, I64 | F64) | (I64 | F64, _) => I64,
_ => panic!("unexpected wasm type for canonical ABI"),
}
}
fn desc(&self) -> &'static str {
match self {
ComponentDefinedType::Record(_) => "record",
ComponentDefinedType::Primitive(_) => "primitive",
ComponentDefinedType::Variant(_) => "variant",
ComponentDefinedType::Tuple(_) => "tuple",
ComponentDefinedType::Enum(_) => "enum",
ComponentDefinedType::Flags(_) => "flags",
ComponentDefinedType::Option(_) => "option",
ComponentDefinedType::List(_) => "list",
ComponentDefinedType::Result { .. } => "result",
ComponentDefinedType::Own(_) => "own",
ComponentDefinedType::Borrow(_) => "borrow",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Copy)]
#[repr(packed(4))] pub struct ResourceId {
globally_unique_id: u64,
contextually_unique_id: u32,
}
#[allow(clippy::large_enum_variant)]
enum TypesKind {
Module(Arc<Module>),
Component(ComponentState),
}
pub struct Types {
list: TypeList,
kind: TypesKind,
}
#[derive(Clone, Copy)]
enum TypesRefKind<'a> {
Module(&'a Module),
Component(&'a ComponentState),
}
#[derive(Clone, Copy)]
pub struct TypesRef<'a> {
list: &'a TypeList,
kind: TypesRefKind<'a>,
}
impl<'a> TypesRef<'a> {
pub(crate) fn from_module(types: &'a TypeList, module: &'a Module) -> Self {
Self {
list: types,
kind: TypesRefKind::Module(module),
}
}
pub(crate) fn from_component(types: &'a TypeList, component: &'a ComponentState) -> Self {
Self {
list: types,
kind: TypesRefKind::Component(component),
}
}
pub fn get(&self, id: TypeId) -> Option<&'a Type> {
self.list.get(id.index)
}
pub fn core_type_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(module) => module.types[index as usize],
TypesRefKind::Component(component) => component.core_types[index as usize],
}
}
pub fn component_type_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(_) => panic!("not a component"),
TypesRefKind::Component(component) => component.types[index as usize],
}
}
pub fn core_type_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(module) => module.types.len() as u32,
TypesRefKind::Component(component) => component.core_types.len() as u32,
}
}
pub fn component_type_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(_module) => 0,
TypesRefKind::Component(component) => component.types.len() as u32,
}
}
pub fn table_at(&self, index: u32) -> TableType {
let tables = match &self.kind {
TypesRefKind::Module(module) => &module.tables,
TypesRefKind::Component(component) => &component.core_tables,
};
tables[index as usize]
}
pub fn table_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(module) => module.tables.len() as u32,
TypesRefKind::Component(component) => component.core_tables.len() as u32,
}
}
pub fn memory_at(&self, index: u32) -> MemoryType {
let memories = match &self.kind {
TypesRefKind::Module(module) => &module.memories,
TypesRefKind::Component(component) => &component.core_memories,
};
memories[index as usize]
}
pub fn memory_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(module) => module.memories.len() as u32,
TypesRefKind::Component(component) => component.core_memories.len() as u32,
}
}
pub fn global_at(&self, index: u32) -> GlobalType {
let globals = match &self.kind {
TypesRefKind::Module(module) => &module.globals,
TypesRefKind::Component(component) => &component.core_globals,
};
globals[index as usize]
}
pub fn global_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(module) => module.globals.len() as u32,
TypesRefKind::Component(component) => component.core_globals.len() as u32,
}
}
pub fn tag_at(&self, index: u32) -> TypeId {
let tags = match &self.kind {
TypesRefKind::Module(module) => &module.tags,
TypesRefKind::Component(component) => &component.core_tags,
};
tags[index as usize]
}
pub fn tag_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(module) => module.tags.len() as u32,
TypesRefKind::Component(component) => component.core_tags.len() as u32,
}
}
pub fn function_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(module) => module.types[module.functions[index as usize] as usize],
TypesRefKind::Component(component) => component.core_funcs[index as usize],
}
}
pub fn function_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(module) => module.functions.len() as u32,
TypesRefKind::Component(component) => component.core_funcs.len() as u32,
}
}
pub fn element_at(&self, index: u32) -> RefType {
match &self.kind {
TypesRefKind::Module(module) => module.element_types[index as usize],
TypesRefKind::Component(_) => {
panic!("no elements on a component")
}
}
}
pub fn element_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(module) => module.element_types.len() as u32,
TypesRefKind::Component(_) => 0,
}
}
pub fn component_function_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(_) => panic!("not a component"),
TypesRefKind::Component(component) => component.funcs[index as usize],
}
}
pub fn component_function_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(_module) => 0,
TypesRefKind::Component(component) => component.funcs.len() as u32,
}
}
pub fn module_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(_) => panic!("not a component"),
TypesRefKind::Component(component) => component.core_modules[index as usize],
}
}
pub fn module_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(_module) => 0,
TypesRefKind::Component(component) => component.core_modules.len() as u32,
}
}
pub fn instance_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(_) => panic!("not a component"),
TypesRefKind::Component(component) => component.core_instances[index as usize],
}
}
pub fn instance_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(_module) => 0,
TypesRefKind::Component(component) => component.core_instances.len() as u32,
}
}
pub fn component_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(_) => panic!("not a component"),
TypesRefKind::Component(component) => component.components[index as usize],
}
}
pub fn component_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(_module) => 0,
TypesRefKind::Component(component) => component.components.len() as u32,
}
}
pub fn component_instance_at(&self, index: u32) -> TypeId {
match &self.kind {
TypesRefKind::Module(_) => panic!("not a component"),
TypesRefKind::Component(component) => component.instances[index as usize],
}
}
pub fn component_instance_count(&self) -> u32 {
match &self.kind {
TypesRefKind::Module(_module) => 0,
TypesRefKind::Component(component) => component.instances.len() as u32,
}
}
pub fn value_at(&self, index: u32) -> ComponentValType {
match &self.kind {
TypesRefKind::Module(_) => panic!("not a component"),
TypesRefKind::Component(component) => component.values[index as usize].0,
}
}
pub fn entity_type_from_import(&self, import: &Import) -> Option<EntityType> {
match &self.kind {
TypesRefKind::Module(module) => Some(match import.ty {
TypeRef::Func(idx) => EntityType::Func(*module.types.get(idx as usize)?),
TypeRef::Table(ty) => EntityType::Table(ty),
TypeRef::Memory(ty) => EntityType::Memory(ty),
TypeRef::Global(ty) => EntityType::Global(ty),
TypeRef::Tag(ty) => EntityType::Tag(*module.types.get(ty.func_type_idx as usize)?),
}),
TypesRefKind::Component(_) => None,
}
}
pub fn entity_type_from_export(&self, export: &Export) -> Option<EntityType> {
match &self.kind {
TypesRefKind::Module(module) => Some(match export.kind {
ExternalKind::Func => EntityType::Func(
module.types[*module.functions.get(export.index as usize)? as usize],
),
ExternalKind::Table => {
EntityType::Table(*module.tables.get(export.index as usize)?)
}
ExternalKind::Memory => {
EntityType::Memory(*module.memories.get(export.index as usize)?)
}
ExternalKind::Global => {
EntityType::Global(*module.globals.get(export.index as usize)?)
}
ExternalKind::Tag => EntityType::Tag(
module.types[*module.functions.get(export.index as usize)? as usize],
),
}),
TypesRefKind::Component(_) => None,
}
}
pub fn component_entity_type_of_import(&self, name: &str) -> Option<ComponentEntityType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => Some(*component.imports.get(name)?),
}
}
pub fn component_entity_type_of_export(&self, name: &str) -> Option<ComponentEntityType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => Some(*component.exports.get(name)?),
}
}
}
impl Index<TypeId> for TypesRef<'_> {
type Output = Type;
fn index(&self, id: TypeId) -> &Type {
&self.list[id.index]
}
}
impl Types {
pub(crate) fn from_module(types: TypeList, module: Arc<Module>) -> Self {
Self {
list: types,
kind: TypesKind::Module(module),
}
}
pub(crate) fn from_component(types: TypeList, component: ComponentState) -> Self {
Self {
list: types,
kind: TypesKind::Component(component),
}
}
pub fn as_ref(&self) -> TypesRef {
TypesRef {
list: &self.list,
kind: match &self.kind {
TypesKind::Module(module) => TypesRefKind::Module(module),
TypesKind::Component(component) => TypesRefKind::Component(component),
},
}
}
pub fn get(&self, id: TypeId) -> Option<&Type> {
self.as_ref().get(id)
}
pub fn core_type_at(&self, index: u32) -> TypeId {
self.as_ref().core_type_at(index)
}
pub fn component_type_at(&self, index: u32) -> TypeId {
self.as_ref().component_type_at(index)
}
pub fn type_count(&self) -> usize {
match &self.kind {
TypesKind::Module(module) => module.types.len(),
TypesKind::Component(component) => component.core_types.len(),
}
}
pub fn table_at(&self, index: u32) -> TableType {
self.as_ref().table_at(index)
}
pub fn table_count(&self) -> usize {
match &self.kind {
TypesKind::Module(module) => module.tables.len(),
TypesKind::Component(component) => component.core_tables.len(),
}
}
pub fn memory_at(&self, index: u32) -> MemoryType {
self.as_ref().memory_at(index)
}
pub fn memory_count(&self) -> u32 {
self.as_ref().memory_count()
}
pub fn global_at(&self, index: u32) -> GlobalType {
self.as_ref().global_at(index)
}
pub fn global_count(&self) -> u32 {
self.as_ref().global_count()
}
pub fn tag_at(&self, index: u32) -> TypeId {
self.as_ref().tag_at(index)
}
pub fn tag_count(&self) -> u32 {
self.as_ref().tag_count()
}
pub fn function_at(&self, index: u32) -> TypeId {
self.as_ref().function_at(index)
}
pub fn function_count(&self) -> u32 {
self.as_ref().function_count()
}
pub fn element_at(&self, index: u32) -> RefType {
self.as_ref().element_at(index)
}
pub fn element_count(&self) -> u32 {
self.as_ref().element_count()
}
pub fn component_function_at(&self, index: u32) -> TypeId {
self.as_ref().component_function_at(index)
}
pub fn component_function_count(&self) -> u32 {
self.as_ref().component_function_count()
}
pub fn module_at(&self, index: u32) -> TypeId {
self.as_ref().module_at(index)
}
pub fn module_count(&self) -> usize {
match &self.kind {
TypesKind::Module(_) => 0,
TypesKind::Component(component) => component.core_modules.len(),
}
}
pub fn instance_at(&self, index: u32) -> TypeId {
self.as_ref().instance_at(index)
}
pub fn instance_count(&self) -> usize {
match &self.kind {
TypesKind::Module(_) => 0,
TypesKind::Component(component) => component.core_instances.len(),
}
}
pub fn component_at(&self, index: u32) -> TypeId {
self.as_ref().component_at(index)
}
pub fn component_count(&self) -> usize {
match &self.kind {
TypesKind::Module(_) => 0,
TypesKind::Component(component) => component.components.len(),
}
}
pub fn component_instance_at(&self, index: u32) -> TypeId {
self.as_ref().component_instance_at(index)
}
pub fn component_instance_count(&self) -> usize {
match &self.kind {
TypesKind::Module(_) => 0,
TypesKind::Component(component) => component.instances.len(),
}
}
pub fn value_at(&self, index: u32) -> ComponentValType {
self.as_ref().value_at(index)
}
pub fn value_count(&self) -> usize {
match &self.kind {
TypesKind::Module(_) => 0,
TypesKind::Component(component) => component.values.len(),
}
}
pub fn entity_type_from_import(&self, import: &Import) -> Option<EntityType> {
self.as_ref().entity_type_from_import(import)
}
pub fn entity_type_from_export(&self, export: &Export) -> Option<EntityType> {
self.as_ref().entity_type_from_export(export)
}
pub fn component_entity_type_of_import(&self, name: &str) -> Option<ComponentEntityType> {
self.as_ref().component_entity_type_of_import(name)
}
pub fn component_entity_type_of_export(&self, name: &str) -> Option<ComponentEntityType> {
self.as_ref().component_entity_type_of_export(name)
}
pub fn peel_alias(&self, ty: TypeId) -> Option<TypeId> {
self.list.peel_alias(ty)
}
}
impl Index<TypeId> for Types {
type Output = Type;
fn index(&self, id: TypeId) -> &Type {
&self.list[id.index]
}
}
pub(crate) struct SnapshotList<T> {
snapshots: Vec<Arc<Snapshot<T>>>,
snapshots_total: usize,
cur: Vec<T>,
unique_mappings: HashMap<u32, u32>,
unique_counter: u32,
}
struct Snapshot<T> {
prior_types: usize,
unique_counter: u32,
unique_mappings: HashMap<u32, u32>,
items: Vec<T>,
}
impl<T> SnapshotList<T> {
pub(crate) fn get(&self, index: usize) -> Option<&T> {
if index >= self.snapshots_total {
return self.cur.get(index - self.snapshots_total);
}
let i = match self
.snapshots
.binary_search_by_key(&index, |snapshot| snapshot.prior_types)
{
Ok(i) => i,
Err(i) => i - 1,
};
let snapshot = &self.snapshots[i];
Some(&snapshot.items[index - snapshot.prior_types])
}
pub(crate) fn push(&mut self, val: T) {
self.cur.push(val);
}
pub(crate) fn len(&self) -> usize {
self.cur.len() + self.snapshots_total
}
pub(crate) fn reserve(&mut self, additional: usize) {
self.cur.reserve(additional);
}
pub(crate) fn commit(&mut self) -> SnapshotList<T> {
let len = self.cur.len();
if len > 0 {
self.unique_counter += 1;
self.cur.shrink_to_fit();
self.snapshots.push(Arc::new(Snapshot {
prior_types: self.snapshots_total,
unique_counter: self.unique_counter - 1,
unique_mappings: mem::take(&mut self.unique_mappings),
items: mem::take(&mut self.cur),
}));
self.snapshots_total += len;
}
SnapshotList {
snapshots: self.snapshots.clone(),
snapshots_total: self.snapshots_total,
unique_mappings: HashMap::new(),
unique_counter: self.unique_counter,
cur: Vec::new(),
}
}
pub fn with_unique(&mut self, mut ty: TypeId) -> TypeId {
self.unique_mappings
.insert(self.unique_counter, ty.unique_id);
ty.unique_id = self.unique_counter;
self.unique_counter += 1;
ty
}
pub fn peel_alias(&self, ty: TypeId) -> Option<TypeId> {
let i = match self
.snapshots
.binary_search_by_key(&ty.unique_id, |snapshot| snapshot.unique_counter)
{
Ok(_) => unreachable!(),
Err(i) => i,
};
let unique_id = match self.snapshots.get(i) {
Some(snapshot) => *snapshot.unique_mappings.get(&ty.unique_id)?,
None => *self.unique_mappings.get(&ty.unique_id)?,
};
Some(TypeId { unique_id, ..ty })
}
}
impl<T> Index<usize> for SnapshotList<T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &T {
self.get(index).unwrap()
}
}
impl<T> Index<TypeId> for SnapshotList<T> {
type Output = T;
#[inline]
fn index(&self, id: TypeId) -> &T {
self.get(id.index).unwrap()
}
}
impl<T> Default for SnapshotList<T> {
fn default() -> SnapshotList<T> {
SnapshotList {
snapshots: Vec::new(),
snapshots_total: 0,
cur: Vec::new(),
unique_counter: 1,
unique_mappings: HashMap::new(),
}
}
}
pub(crate) type TypeList = SnapshotList<Type>;
pub(crate) struct TypeAlloc {
list: TypeList,
globally_unique_id: u64,
next_resource_id: u32,
}
impl Default for TypeAlloc {
fn default() -> TypeAlloc {
static NEXT_GLOBAL_ID: AtomicU64 = AtomicU64::new(0);
TypeAlloc {
list: TypeList::default(),
globally_unique_id: NEXT_GLOBAL_ID.fetch_add(1, Ordering::Relaxed),
next_resource_id: 0,
}
}
}
impl Deref for TypeAlloc {
type Target = TypeList;
fn deref(&self) -> &TypeList {
&self.list
}
}
impl DerefMut for TypeAlloc {
fn deref_mut(&mut self) -> &mut TypeList {
&mut self.list
}
}
impl TypeAlloc {
pub fn push_ty(&mut self, ty: Type) -> TypeId {
let index = self.list.len();
let info = ty.info();
self.list.push(ty);
TypeId {
index,
info,
unique_id: 0,
}
}
pub fn alloc_resource_id(&mut self) -> ResourceId {
let contextually_unique_id = self.next_resource_id;
self.next_resource_id = self.next_resource_id.checked_add(1).unwrap();
ResourceId {
globally_unique_id: self.globally_unique_id,
contextually_unique_id,
}
}
pub fn free_variables_type_id(&self, id: TypeId, set: &mut IndexSet<ResourceId>) {
match &self[id] {
Type::Sub(_) | Type::Module(_) | Type::Instance(_) => {}
Type::Component(i) => {
for ty in i.imports.values().chain(i.exports.values()) {
self.free_variables_component_entity(ty, set);
}
for (id, _path) in i.imported_resources.iter().chain(&i.defined_resources) {
set.remove(id);
}
}
Type::ComponentInstance(i) => {
for ty in i.exports.values() {
self.free_variables_component_entity(ty, set);
}
for id in i.defined_resources.iter() {
set.remove(id);
}
}
Type::Resource(r) => {
set.insert(*r);
}
Type::ComponentFunc(i) => {
for ty in i
.params
.iter()
.map(|(_, ty)| ty)
.chain(i.results.iter().map(|(_, ty)| ty))
{
self.free_variables_valtype(ty, set);
}
}
Type::Defined(i) => match i {
ComponentDefinedType::Primitive(_)
| ComponentDefinedType::Flags(_)
| ComponentDefinedType::Enum(_) => {}
ComponentDefinedType::Record(r) => {
for ty in r.fields.values() {
self.free_variables_valtype(ty, set);
}
}
ComponentDefinedType::Tuple(r) => {
for ty in r.types.iter() {
self.free_variables_valtype(ty, set);
}
}
ComponentDefinedType::Variant(r) => {
for ty in r.cases.values() {
if let Some(ty) = &ty.ty {
self.free_variables_valtype(ty, set);
}
}
}
ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
self.free_variables_valtype(ty, set);
}
ComponentDefinedType::Result { ok, err } => {
if let Some(ok) = ok {
self.free_variables_valtype(ok, set);
}
if let Some(err) = err {
self.free_variables_valtype(err, set);
}
}
ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
self.free_variables_type_id(*id, set);
}
},
}
}
pub fn free_variables_component_entity(
&self,
ty: &ComponentEntityType,
set: &mut IndexSet<ResourceId>,
) {
match ty {
ComponentEntityType::Module(id)
| ComponentEntityType::Func(id)
| ComponentEntityType::Instance(id)
| ComponentEntityType::Component(id) => self.free_variables_type_id(*id, set),
ComponentEntityType::Type { created, .. } => {
self.free_variables_type_id(*created, set);
}
ComponentEntityType::Value(ty) => self.free_variables_valtype(ty, set),
}
}
fn free_variables_valtype(&self, ty: &ComponentValType, set: &mut IndexSet<ResourceId>) {
match ty {
ComponentValType::Primitive(_) => {}
ComponentValType::Type(id) => self.free_variables_type_id(*id, set),
}
}
pub(crate) fn type_named_type_id(&self, id: TypeId, set: &HashSet<TypeId>) -> bool {
let ty = self[id].unwrap_defined();
match ty {
ComponentDefinedType::Primitive(_) => true,
ComponentDefinedType::Flags(_)
| ComponentDefinedType::Enum(_)
| ComponentDefinedType::Record(_)
| ComponentDefinedType::Variant(_) => set.contains(&id),
ComponentDefinedType::Tuple(r) => {
r.types.iter().all(|t| self.type_named_valtype(t, set))
}
ComponentDefinedType::Result { ok, err } => {
ok.as_ref()
.map(|t| self.type_named_valtype(t, set))
.unwrap_or(true)
&& err
.as_ref()
.map(|t| self.type_named_valtype(t, set))
.unwrap_or(true)
}
ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
self.type_named_valtype(ty, set)
}
ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => set.contains(id),
}
}
pub(crate) fn type_named_valtype(&self, ty: &ComponentValType, set: &HashSet<TypeId>) -> bool {
match ty {
ComponentValType::Primitive(_) => true,
ComponentValType::Type(id) => self.type_named_type_id(*id, set),
}
}
}
pub(crate) trait Remap: Index<TypeId, Output = Type> {
fn push_ty(&mut self, ty: Type) -> TypeId;
fn remap_type_id(&mut self, id: &mut TypeId, map: &mut Remapping) -> bool {
if let Some(new) = map.types.get(id) {
let changed = *new != *id;
*id = *new;
return changed;
}
let mut any_changed = false;
let map_map = |tmp: &mut IndexMap<ResourceId, Vec<usize>>,
any_changed: &mut bool,
map: &mut Remapping| {
for (id, path) in mem::take(tmp) {
let id = match map.resources.get(&id) {
Some(id) => {
*any_changed = true;
*id
}
None => id,
};
tmp.insert(id, path);
}
};
let ty = match &self[*id] {
Type::Sub(_) | Type::Module(_) | Type::Instance(_) => return false,
Type::Component(i) => {
let mut tmp = i.clone();
for ty in tmp.imports.values_mut().chain(tmp.exports.values_mut()) {
if self.remap_component_entity(ty, map) {
any_changed = true;
}
}
for (id, _) in tmp
.imported_resources
.iter_mut()
.chain(&mut tmp.defined_resources)
{
if let Some(new) = map.resources.get(id) {
*id = *new;
any_changed = true;
}
}
map_map(&mut tmp.explicit_resources, &mut any_changed, map);
Type::Component(tmp)
}
Type::ComponentInstance(i) => {
let mut tmp = i.clone();
for ty in tmp.exports.values_mut() {
if self.remap_component_entity(ty, map) {
any_changed = true;
}
}
for id in tmp.defined_resources.iter_mut() {
if let Some(new) = map.resources.get(id) {
*id = *new;
any_changed = true;
}
}
map_map(&mut tmp.explicit_resources, &mut any_changed, map);
Type::ComponentInstance(tmp)
}
Type::Resource(id) => {
let id = match map.resources.get(id).copied() {
Some(id) => id,
None => return false,
};
any_changed = true;
Type::Resource(id)
}
Type::ComponentFunc(i) => {
let mut tmp = i.clone();
for ty in tmp
.params
.iter_mut()
.map(|(_, ty)| ty)
.chain(tmp.results.iter_mut().map(|(_, ty)| ty))
{
if self.remap_valtype(ty, map) {
any_changed = true;
}
}
Type::ComponentFunc(tmp)
}
Type::Defined(i) => {
let mut tmp = i.clone();
match &mut tmp {
ComponentDefinedType::Primitive(_)
| ComponentDefinedType::Flags(_)
| ComponentDefinedType::Enum(_) => {}
ComponentDefinedType::Record(r) => {
for ty in r.fields.values_mut() {
if self.remap_valtype(ty, map) {
any_changed = true;
}
}
}
ComponentDefinedType::Tuple(r) => {
for ty in r.types.iter_mut() {
if self.remap_valtype(ty, map) {
any_changed = true;
}
}
}
ComponentDefinedType::Variant(r) => {
for ty in r.cases.values_mut() {
if let Some(ty) = &mut ty.ty {
if self.remap_valtype(ty, map) {
any_changed = true;
}
}
}
}
ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
if self.remap_valtype(ty, map) {
any_changed = true;
}
}
ComponentDefinedType::Result { ok, err } => {
if let Some(ok) = ok {
if self.remap_valtype(ok, map) {
any_changed = true;
}
}
if let Some(err) = err {
if self.remap_valtype(err, map) {
any_changed = true;
}
}
}
ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
if self.remap_type_id(id, map) {
any_changed = true;
}
}
}
Type::Defined(tmp)
}
};
let new = if any_changed { self.push_ty(ty) } else { *id };
let prev = map.types.insert(*id, new);
assert!(prev.is_none());
let changed = *id != new;
*id = new;
changed
}
fn remap_component_entity(
&mut self,
ty: &mut ComponentEntityType,
map: &mut Remapping,
) -> bool {
match ty {
ComponentEntityType::Module(id)
| ComponentEntityType::Func(id)
| ComponentEntityType::Instance(id)
| ComponentEntityType::Component(id) => self.remap_type_id(id, map),
ComponentEntityType::Type {
referenced,
created,
} => {
let changed = self.remap_type_id(referenced, map);
if *referenced == *created {
*created = *referenced;
changed
} else {
self.remap_type_id(created, map) || changed
}
}
ComponentEntityType::Value(ty) => self.remap_valtype(ty, map),
}
}
fn remap_valtype(&mut self, ty: &mut ComponentValType, map: &mut Remapping) -> bool {
match ty {
ComponentValType::Primitive(_) => false,
ComponentValType::Type(id) => self.remap_type_id(id, map),
}
}
}
#[derive(Default)]
pub(crate) struct Remapping {
pub(crate) resources: HashMap<ResourceId, ResourceId>,
types: HashMap<TypeId, TypeId>,
}
impl Remap for TypeAlloc {
fn push_ty(&mut self, ty: Type) -> TypeId {
<TypeAlloc>::push_ty(self, ty)
}
}
impl Index<TypeId> for TypeAlloc {
type Output = Type;
#[inline]
fn index(&self, id: TypeId) -> &Type {
&self.list[id]
}
}
pub(crate) struct SubtypeCx<'a> {
pub(crate) a: SubtypeArena<'a>,
pub(crate) b: SubtypeArena<'a>,
}
impl<'a> SubtypeCx<'a> {
pub(crate) fn new(a: &'a TypeList, b: &'a TypeList) -> SubtypeCx<'a> {
SubtypeCx {
a: SubtypeArena::new(a),
b: SubtypeArena::new(b),
}
}
fn swap(&mut self) {
mem::swap(&mut self.a, &mut self.b);
}
fn mark<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
let a_len = self.a.list.len();
let b_len = self.b.list.len();
let result = f(self);
self.a.list.truncate(a_len);
self.b.list.truncate(b_len);
result
}
pub fn component_entity_type(
&mut self,
a: &ComponentEntityType,
b: &ComponentEntityType,
offset: usize,
) -> Result<()> {
use ComponentEntityType::*;
match (a, b) {
(Module(a), Module(b)) => {
self.swap();
let a_imports = &self.b[*a].unwrap_module().imports;
let b_imports = &self.a[*b].unwrap_module().imports;
for (k, a) in a_imports {
match b_imports.get(k) {
Some(b) => self.entity_type(b, a, offset).with_context(|| {
format!("type mismatch in import `{}::{}`", k.0, k.1)
})?,
None => bail!(offset, "missing expected import `{}::{}`", k.0, k.1),
}
}
self.swap();
let a = self.a[*a].unwrap_module();
let b = self.b[*b].unwrap_module();
for (k, b) in b.exports.iter() {
match a.exports.get(k) {
Some(a) => self
.entity_type(a, b, offset)
.with_context(|| format!("type mismatch in export `{k}`"))?,
None => bail!(offset, "missing expected export `{k}`"),
}
}
Ok(())
}
(Module(_), b) => bail!(offset, "expected {}, found module", b.desc()),
(Func(a), Func(b)) => {
let a = self.a[*a].unwrap_component_func();
let b = self.b[*b].unwrap_component_func();
if a.params.len() != b.params.len() {
bail!(
offset,
"expected {} parameters, found {}",
b.params.len(),
a.params.len(),
);
}
if a.results.len() != b.results.len() {
bail!(
offset,
"expected {} results, found {}",
b.results.len(),
a.results.len(),
);
}
for ((an, a), (bn, b)) in a.params.iter().zip(b.params.iter()) {
if an != bn {
bail!(offset, "expected parameter named `{bn}`, found `{an}`");
}
self.component_val_type(a, b, offset)
.with_context(|| format!("type mismatch in function parameter `{an}`"))?;
}
for ((an, a), (bn, b)) in a.results.iter().zip(b.results.iter()) {
if an != bn {
bail!(offset, "mismatched result names");
}
self.component_val_type(a, b, offset)
.with_context(|| "type mismatch with result type")?;
}
Ok(())
}
(Func(_), b) => bail!(offset, "expected {}, found func", b.desc()),
(Value(a), Value(b)) => self.component_val_type(a, b, offset),
(Value(_), b) => bail!(offset, "expected {}, found value", b.desc()),
(Type { referenced: a, .. }, Type { referenced: b, .. }) => {
use self::Type::*;
match (&self.a[*a], &self.b[*b]) {
(Defined(a), Defined(b)) => self.component_defined_type(a, b, offset),
(Defined(_), Resource(_)) => bail!(offset, "expected resource, found type"),
(Resource(a), Resource(b)) => {
if a == b {
Ok(())
} else {
bail!(offset, "resource types are not the same")
}
}
(Resource(_), Defined(_)) => bail!(offset, "expected type, found resource"),
_ => unreachable!(),
}
}
(Type { .. }, b) => bail!(offset, "expected {}, found type", b.desc()),
(Instance(a_id), Instance(b_id)) => {
let a = self.a[*a_id].unwrap_component_instance();
let b = self.b[*b_id].unwrap_component_instance();
let mut exports = Vec::with_capacity(b.exports.len());
for (k, b) in b.exports.iter() {
match a.exports.get(k) {
Some(a) => exports.push((*a, *b)),
None => bail!(offset, "missing expected export `{k}`"),
}
}
for (i, (a, b)) in exports.iter().enumerate() {
let err = match self.component_entity_type(a, b, offset) {
Ok(()) => continue,
Err(e) => e,
};
let (name, _) = self.b[*b_id]
.unwrap_component_instance()
.exports
.get_index(i)
.unwrap();
return Err(
err.with_context(|| format!("type mismatch in instance export `{name}`"))
);
}
Ok(())
}
(Instance(_), b) => bail!(offset, "expected {}, found instance", b.desc()),
(Component(a), Component(b)) => {
let b_imports = self.b[*b]
.unwrap_component()
.imports
.iter()
.map(|(name, ty)| (name.clone(), ty.clone()))
.collect();
self.swap();
let mut import_mapping =
self.open_instance_type(&b_imports, *a, ExternKind::Import, offset)?;
self.swap();
self.mark(|this| {
let mut a_exports = this.a[*a]
.unwrap_component()
.exports
.iter()
.map(|(name, ty)| (name.clone(), ty.clone()))
.collect::<IndexMap<_, _>>();
for ty in a_exports.values_mut() {
this.a.remap_component_entity(ty, &mut import_mapping);
}
this.open_instance_type(&a_exports, *b, ExternKind::Export, offset)?;
Ok(())
})
}
(Component(_), b) => bail!(offset, "expected {}, found component", b.desc()),
}
}
pub fn open_instance_type(
&mut self,
a: &IndexMap<String, ComponentEntityType>,
b: TypeId,
kind: ExternKind,
offset: usize,
) -> Result<Remapping> {
let component_type = self.b[b].unwrap_component();
let entities = match kind {
ExternKind::Import => &component_type.imports,
ExternKind::Export => &component_type.exports,
};
let resources = match kind {
ExternKind::Import => &component_type.imported_resources,
ExternKind::Export => &component_type.defined_resources,
};
let mut mapping = Remapping::default();
'outer: for (resource, path) in resources.iter() {
let (name, ty) = entities.get_index(path[0]).unwrap();
let mut ty = *ty;
let mut arg = a.get(name);
for i in path.iter().skip(1).copied() {
let id = match ty {
ComponentEntityType::Instance(id) => id,
_ => unreachable!(),
};
let (name, next_ty) = self.b[id]
.unwrap_component_instance()
.exports
.get_index(i)
.unwrap();
ty = *next_ty;
arg = match arg {
Some(ComponentEntityType::Instance(id)) => {
self.a[*id].unwrap_component_instance().exports.get(name)
}
_ => continue 'outer,
};
}
if cfg!(debug_assertions) {
let id = match ty {
ComponentEntityType::Type { created, .. } => match &self.a[created] {
Type::Resource(r) => *r,
_ => unreachable!(),
},
_ => unreachable!(),
};
assert_eq!(id, *resource);
}
if let Some(ComponentEntityType::Type { created, .. }) = arg {
if let Type::Resource(r) = &self.b[*created] {
mapping.resources.insert(*resource, *r);
}
}
}
let mut to_typecheck = Vec::new();
for (name, expected) in entities.iter() {
match a.get(name) {
Some(arg) => to_typecheck.push((arg.clone(), expected.clone())),
None => bail!(offset, "missing {} named `{name}`", kind.desc()),
}
}
let mut type_map = HashMap::new();
for (i, (actual, expected)) in to_typecheck.into_iter().enumerate() {
let result = self.mark(|this| {
let mut expected = expected;
this.b.remap_component_entity(&mut expected, &mut mapping);
mapping.types.clear();
this.component_entity_type(&actual, &expected, offset)
});
let err = match result {
Ok(()) => {
self.register_type_renamings(actual, expected, &mut type_map);
continue;
}
Err(e) => e,
};
let component_type = self.b[b].unwrap_component();
let entities = match kind {
ExternKind::Import => &component_type.imports,
ExternKind::Export => &component_type.exports,
};
let (name, _) = entities.get_index(i).unwrap();
return Err(err.with_context(|| format!("type mismatch for {} `{name}`", kind.desc())));
}
mapping.types = type_map;
Ok(mapping)
}
pub(crate) fn entity_type(&self, a: &EntityType, b: &EntityType, offset: usize) -> Result<()> {
macro_rules! limits_match {
($a:expr, $b:expr) => {{
let a = $a;
let b = $b;
a.initial >= b.initial
&& match b.maximum {
Some(b_max) => match a.maximum {
Some(a_max) => a_max <= b_max,
None => false,
},
None => true,
}
}};
}
match (a, b) {
(EntityType::Func(a), EntityType::Func(b)) => {
self.func_type(self.a[*a].unwrap_func(), self.b[*b].unwrap_func(), offset)
}
(EntityType::Func(_), b) => bail!(offset, "expected {}, found func", b.desc()),
(EntityType::Table(a), EntityType::Table(b)) => {
if a.element_type != b.element_type {
bail!(
offset,
"expected table element type {}, found {}",
b.element_type,
a.element_type,
)
}
if limits_match!(a, b) {
Ok(())
} else {
bail!(offset, "mismatch in table limits")
}
}
(EntityType::Table(_), b) => bail!(offset, "expected {}, found table", b.desc()),
(EntityType::Memory(a), EntityType::Memory(b)) => {
if a.shared != b.shared {
bail!(offset, "mismatch in the shared flag for memories")
}
if a.memory64 != b.memory64 {
bail!(offset, "mismatch in index type used for memories")
}
if limits_match!(a, b) {
Ok(())
} else {
bail!(offset, "mismatch in memory limits")
}
}
(EntityType::Memory(_), b) => bail!(offset, "expected {}, found memory", b.desc()),
(EntityType::Global(a), EntityType::Global(b)) => {
if a.mutable != b.mutable {
bail!(offset, "global types differ in mutability")
}
if a.content_type == b.content_type {
Ok(())
} else {
bail!(
offset,
"expected global type {}, found {}",
b.content_type,
a.content_type,
)
}
}
(EntityType::Global(_), b) => bail!(offset, "expected {}, found global", b.desc()),
(EntityType::Tag(a), EntityType::Tag(b)) => {
self.func_type(self.a[*a].unwrap_func(), self.b[*b].unwrap_func(), offset)
}
(EntityType::Tag(_), b) => bail!(offset, "expected {}, found tag", b.desc()),
}
}
fn func_type(&self, a: &FuncType, b: &FuncType, offset: usize) -> Result<()> {
if a == b {
Ok(())
} else {
bail!(
offset,
"expected: {}\n\
found: {}",
b.desc(),
a.desc(),
)
}
}
pub(crate) fn component_val_type(
&self,
a: &ComponentValType,
b: &ComponentValType,
offset: usize,
) -> Result<()> {
match (a, b) {
(ComponentValType::Primitive(a), ComponentValType::Primitive(b)) => {
self.primitive_val_type(*a, *b, offset)
}
(ComponentValType::Type(a), ComponentValType::Type(b)) => self.component_defined_type(
self.a[*a].unwrap_defined(),
self.b[*b].unwrap_defined(),
offset,
),
(ComponentValType::Primitive(a), ComponentValType::Type(b)) => {
match self.b[*b].unwrap_defined() {
ComponentDefinedType::Primitive(b) => self.primitive_val_type(*a, *b, offset),
b => bail!(offset, "expected {}, found {a}", b.desc()),
}
}
(ComponentValType::Type(a), ComponentValType::Primitive(b)) => {
match self.a[*a].unwrap_defined() {
ComponentDefinedType::Primitive(a) => self.primitive_val_type(*a, *b, offset),
a => bail!(offset, "expected {b}, found {}", a.desc()),
}
}
}
}
fn component_defined_type(
&self,
a: &ComponentDefinedType,
b: &ComponentDefinedType,
offset: usize,
) -> Result<()> {
use ComponentDefinedType::*;
match (a, b) {
(Primitive(a), Primitive(b)) => self.primitive_val_type(*a, *b, offset),
(Primitive(a), b) => bail!(offset, "expected {}, found {a}", b.desc()),
(Record(a), Record(b)) => {
if a.fields.len() != b.fields.len() {
bail!(
offset,
"expected {} fields, found {}",
b.fields.len(),
a.fields.len(),
);
}
for ((aname, a), (bname, b)) in a.fields.iter().zip(b.fields.iter()) {
if aname != bname {
bail!(offset, "expected field name `{bname}`, found `{aname}`");
}
self.component_val_type(a, b, offset)
.with_context(|| format!("type mismatch in record field `{aname}`"))?;
}
Ok(())
}
(Record(_), b) => bail!(offset, "expected {}, found record", b.desc()),
(Variant(a), Variant(b)) => {
if a.cases.len() != b.cases.len() {
bail!(
offset,
"expected {} cases, found {}",
b.cases.len(),
a.cases.len(),
);
}
for ((aname, a), (bname, b)) in a.cases.iter().zip(b.cases.iter()) {
if aname != bname {
bail!(offset, "expected case named `{bname}`, found `{aname}`");
}
match (&a.ty, &b.ty) {
(Some(a), Some(b)) => self
.component_val_type(a, b, offset)
.with_context(|| format!("type mismatch in variant case `{aname}`"))?,
(None, None) => {}
(None, Some(_)) => {
bail!(offset, "expected case `{aname}` to have a type, found none")
}
(Some(_), None) => bail!(offset, "expected case `{aname}` to have no type"),
}
}
Ok(())
}
(Variant(_), b) => bail!(offset, "expected {}, found variant", b.desc()),
(List(a), List(b)) | (Option(a), Option(b)) => self.component_val_type(a, b, offset),
(List(_), b) => bail!(offset, "expected {}, found list", b.desc()),
(Option(_), b) => bail!(offset, "expected {}, found option", b.desc()),
(Tuple(a), Tuple(b)) => {
if a.types.len() != b.types.len() {
bail!(
offset,
"expected {} types, found {}",
b.types.len(),
a.types.len(),
);
}
for (i, (a, b)) in a.types.iter().zip(b.types.iter()).enumerate() {
self.component_val_type(a, b, offset)
.with_context(|| format!("type mismatch in tuple field {i}"))?;
}
Ok(())
}
(Tuple(_), b) => bail!(offset, "expected {}, found tuple", b.desc()),
(at @ Flags(a), Flags(b)) | (at @ Enum(a), Enum(b)) => {
let desc = match at {
Flags(_) => "flags",
_ => "enum",
};
if a.len() == b.len() && a.iter().eq(b.iter()) {
Ok(())
} else {
bail!(offset, "mismatch in {desc} elements")
}
}
(Flags(_), b) => bail!(offset, "expected {}, found flags", b.desc()),
(Enum(_), b) => bail!(offset, "expected {}, found enum", b.desc()),
(Result { ok: ao, err: ae }, Result { ok: bo, err: be }) => {
match (ao, bo) {
(None, None) => {}
(Some(a), Some(b)) => self
.component_val_type(a, b, offset)
.with_context(|| "type mismatch in ok variant")?,
(None, Some(_)) => bail!(offset, "expected ok type, but found none"),
(Some(_), None) => bail!(offset, "expected ok type to not be present"),
}
match (ae, be) {
(None, None) => {}
(Some(a), Some(b)) => self
.component_val_type(a, b, offset)
.with_context(|| "type mismatch in err variant")?,
(None, Some(_)) => bail!(offset, "expected err type, but found none"),
(Some(_), None) => bail!(offset, "expected err type to not be present"),
}
Ok(())
}
(Result { .. }, b) => bail!(offset, "expected {}, found result", b.desc()),
(Own(a), Own(b)) | (Borrow(a), Borrow(b)) => {
let a = self.a[*a].unwrap_resource();
let b = self.b[*b].unwrap_resource();
if a == b {
Ok(())
} else {
bail!(offset, "resource types are not the same")
}
}
(Own(_), b) => bail!(offset, "expected {}, found own", b.desc()),
(Borrow(_), b) => bail!(offset, "expected {}, found borrow", b.desc()),
}
}
fn primitive_val_type(
&self,
a: PrimitiveValType,
b: PrimitiveValType,
offset: usize,
) -> Result<()> {
if a == b {
Ok(())
} else {
bail!(offset, "expected primitive `{b}` found primitive `{a}`")
}
}
fn register_type_renamings(
&self,
actual: ComponentEntityType,
expected: ComponentEntityType,
type_map: &mut HashMap<TypeId, TypeId>,
) {
match (expected, actual) {
(
ComponentEntityType::Type {
created: expected, ..
},
ComponentEntityType::Type {
created: actual, ..
},
) => {
let prev = type_map.insert(expected, actual);
assert!(prev.is_none());
}
(ComponentEntityType::Instance(expected), ComponentEntityType::Instance(actual)) => {
let actual = self.a[actual].unwrap_component_instance();
for (name, expected) in self.b[expected].unwrap_component_instance().exports.iter()
{
let actual = actual.exports[name];
self.register_type_renamings(actual, *expected, type_map);
}
}
_ => {}
}
}
}
pub(crate) struct SubtypeArena<'a> {
types: &'a TypeList,
list: Vec<Type>,
}
impl<'a> SubtypeArena<'a> {
fn new(types: &'a TypeList) -> SubtypeArena<'a> {
SubtypeArena {
types,
list: Vec::new(),
}
}
}
impl Index<TypeId> for SubtypeArena<'_> {
type Output = Type;
fn index(&self, id: TypeId) -> &Type {
if id.index < self.types.len() {
&self.types[id]
} else {
&self.list[id.index - self.types.len()]
}
}
}
impl Remap for SubtypeArena<'_> {
fn push_ty(&mut self, ty: Type) -> TypeId {
let index = self.list.len() + self.types.len();
let info = ty.info();
self.list.push(ty);
TypeId {
index,
info,
unique_id: 0,
}
}
}
pub(crate) trait Context {
fn with_context<S>(self, context: impl FnOnce() -> S) -> Self
where
S: Into<String>;
}
impl<T> Context for Result<T> {
fn with_context<S>(self, context: impl FnOnce() -> S) -> Self
where
S: Into<String>,
{
match self {
Ok(val) => Ok(val),
Err(e) => Err(e.with_context(context)),
}
}
}
impl Context for BinaryReaderError {
fn with_context<S>(mut self, context: impl FnOnce() -> S) -> Self
where
S: Into<String>,
{
self.add_context(context().into());
self
}
}