use super::{component::ComponentState, core::Module};
use crate::{
ComponentExport, ComponentImport, Export, ExternalKind, FuncType, GlobalType, Import,
MemoryType, PrimitiveValType, TableType, TypeRef, ValType,
};
use indexmap::{IndexMap, IndexSet};
use std::collections::HashMap;
use std::{
borrow::Borrow,
fmt,
hash::{Hash, Hasher},
mem,
ops::{Deref, DerefMut},
sync::Arc,
};
use url::Url;
const MAX_FLAT_FUNC_PARAMS: usize = 16;
const MAX_FLAT_FUNC_RESULTS: usize = 1;
const MAX_LOWERED_TYPES: usize = MAX_FLAT_FUNC_PARAMS + 1;
#[derive(Debug, Eq)]
#[repr(transparent)]
pub struct KebabStr(str);
impl KebabStr {
pub fn new<'a>(s: impl AsRef<str> + 'a) -> Option<&'a Self> {
let s = Self::new_unchecked(s);
if s.is_kebab_case() {
Some(s)
} else {
None
}
}
pub(crate) fn new_unchecked<'a>(s: impl AsRef<str> + 'a) -> &'a Self {
unsafe { std::mem::transmute::<_, &Self>(s.as_ref()) }
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn to_kebab_string(&self) -> KebabString {
KebabString(self.to_string())
}
fn is_kebab_case(&self) -> bool {
let mut lower = false;
let mut upper = false;
for c in self.chars() {
match c {
'a'..='z' if !lower && !upper => lower = true,
'A'..='Z' if !lower && !upper => upper = true,
'a'..='z' if lower => {}
'A'..='Z' if upper => {}
'0'..='9' if lower || upper => {}
'-' if lower || upper => {
lower = false;
upper = false;
}
_ => return false,
}
}
!self.is_empty() && !self.ends_with('-')
}
}
impl Deref for KebabStr {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl PartialEq for KebabStr {
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
self.chars()
.zip(other.chars())
.all(|(a, b)| a.to_ascii_lowercase() == b.to_ascii_lowercase())
}
}
impl PartialEq<KebabString> for KebabStr {
fn eq(&self, other: &KebabString) -> bool {
self.eq(other.as_kebab_str())
}
}
impl Hash for KebabStr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.len().hash(state);
for b in self.chars() {
b.to_ascii_lowercase().hash(state);
}
}
}
impl fmt::Display for KebabStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self as &str).fmt(f)
}
}
impl ToOwned for KebabStr {
type Owned = KebabString;
fn to_owned(&self) -> Self::Owned {
self.to_kebab_string()
}
}
#[derive(Debug, Clone, Eq)]
pub struct KebabString(String);
impl KebabString {
pub fn new(s: impl Into<String>) -> Option<Self> {
let s = s.into();
if KebabStr::new(&s).is_some() {
Some(Self(s))
} else {
None
}
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn as_kebab_str(&self) -> &KebabStr {
KebabStr::new_unchecked(self.as_str())
}
}
impl Deref for KebabString {
type Target = KebabStr;
fn deref(&self) -> &Self::Target {
self.as_kebab_str()
}
}
impl Borrow<KebabStr> for KebabString {
fn borrow(&self) -> &KebabStr {
self.as_kebab_str()
}
}
impl PartialEq for KebabString {
fn eq(&self, other: &Self) -> bool {
self.as_kebab_str().eq(other.as_kebab_str())
}
}
impl PartialEq<KebabStr> for KebabString {
fn eq(&self, other: &KebabStr) -> bool {
self.as_kebab_str().eq(other)
}
}
impl Hash for KebabString {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_kebab_str().hash(state)
}
}
impl fmt::Display for KebabString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_kebab_str().fmt(f)
}
}
impl From<KebabString> for String {
fn from(s: KebabString) -> String {
s.0
}
}
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)]
pub struct TypeId {
pub(crate) index: usize,
pub(crate) type_size: u32,
unique_id: u32,
}
const _: () = {
assert!(std::mem::size_of::<TypeId>() <= 16);
};
#[derive(Debug)]
pub enum Type {
Func(FuncType),
Module(ModuleType),
Instance(InstanceType),
Component(ComponentType),
ComponentInstance(ComponentInstanceType),
ComponentFunc(ComponentFuncType),
Defined(ComponentDefinedType),
}
impl Type {
pub fn as_func_type(&self) -> Option<&FuncType> {
match self {
Self::Func(ty) => Some(ty),
_ => None,
}
}
pub fn as_module_type(&self) -> Option<&ModuleType> {
match self {
Self::Module(ty) => Some(ty),
_ => None,
}
}
pub fn as_instance_type(&self) -> Option<&InstanceType> {
match self {
Self::Instance(ty) => Some(ty),
_ => None,
}
}
pub fn as_component_type(&self) -> Option<&ComponentType> {
match self {
Self::Component(ty) => Some(ty),
_ => None,
}
}
pub fn as_component_instance_type(&self) -> Option<&ComponentInstanceType> {
match self {
Self::ComponentInstance(ty) => Some(ty),
_ => None,
}
}
pub fn as_component_func_type(&self) -> Option<&ComponentFuncType> {
match self {
Self::ComponentFunc(ty) => Some(ty),
_ => None,
}
}
pub fn as_defined_type(&self) -> Option<&ComponentDefinedType> {
match self {
Self::Defined(ty) => Some(ty),
_ => None,
}
}
pub(crate) fn type_size(&self) -> u32 {
match self {
Self::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32,
Self::Module(ty) => ty.type_size,
Self::Instance(ty) => ty.type_size,
Self::Component(ty) => ty.type_size,
Self::ComponentInstance(ty) => ty.type_size,
Self::ComponentFunc(ty) => ty.type_size,
Self::Defined(ty) => ty.type_size(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ComponentValType {
Primitive(PrimitiveValType),
Type(TypeId),
}
impl ComponentValType {
pub(crate) fn requires_realloc(&self, types: &TypeList) -> bool {
match self {
ComponentValType::Primitive(ty) => ty.requires_realloc(),
ComponentValType::Type(ty) => types[*ty]
.as_defined_type()
.unwrap()
.requires_realloc(types),
}
}
pub(crate) fn is_optional(&self, types: &TypeList) -> bool {
match self {
ComponentValType::Primitive(_) => false,
ComponentValType::Type(ty) => {
matches!(
types[*ty].as_defined_type().unwrap(),
ComponentDefinedType::Option(_)
)
}
}
}
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
match (a, b) {
(ComponentValType::Primitive(a), ComponentValType::Primitive(b)) => {
PrimitiveValType::is_subtype_of(*a, *b)
}
(ComponentValType::Type(a), ComponentValType::Type(b)) => {
ComponentDefinedType::internal_is_subtype_of(
at[*a].as_defined_type().unwrap(),
at,
bt[*b].as_defined_type().unwrap(),
bt,
)
}
(ComponentValType::Primitive(a), ComponentValType::Type(b)) => {
match bt[*b].as_defined_type().unwrap() {
ComponentDefinedType::Primitive(b) => PrimitiveValType::is_subtype_of(*a, *b),
_ => false,
}
}
(ComponentValType::Type(a), ComponentValType::Primitive(b)) => {
match at[*a].as_defined_type().unwrap() {
ComponentDefinedType::Primitive(a) => PrimitiveValType::is_subtype_of(*a, *b),
_ => false,
}
}
}
}
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]
.as_defined_type()
.unwrap()
.push_wasm_types(types, lowered_types),
}
}
pub(crate) fn type_size(&self) -> u32 {
match self {
Self::Primitive(_) => 1,
Self::Type(id) => id.type_size,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum EntityType {
Func(TypeId),
Table(TableType),
Memory(MemoryType),
Global(GlobalType),
Tag(TypeId),
}
impl EntityType {
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
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)) => {
at[*a].as_func_type().unwrap() == bt[*b].as_func_type().unwrap()
}
(EntityType::Table(a), EntityType::Table(b)) => {
a.element_type == b.element_type && limits_match!(a, b)
}
(EntityType::Memory(a), EntityType::Memory(b)) => {
a.shared == b.shared && a.memory64 == b.memory64 && limits_match!(a, b)
}
(EntityType::Global(a), EntityType::Global(b)) => a == b,
(EntityType::Tag(a), EntityType::Tag(b)) => {
at[*a].as_func_type().unwrap() == bt[*b].as_func_type().unwrap()
}
_ => false,
}
}
pub(crate) fn desc(&self) -> &'static str {
match self {
Self::Func(_) => "function",
Self::Table(_) => "table",
Self::Memory(_) => "memory",
Self::Global(_) => "global",
Self::Tag(_) => "tag",
}
}
pub(crate) fn type_size(&self) -> u32 {
match self {
Self::Func(id) | Self::Tag(id) => id.type_size,
Self::Table(_) | Self::Memory(_) | Self::Global(_) => 1,
}
}
}
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) type_size: u32,
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)
}
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
a.imports.iter().all(|(k, a)| match b.imports.get(k) {
Some(b) => EntityType::internal_is_subtype_of(b, bt, a, at),
None => false,
}) && b.exports.iter().all(|(k, b)| match a.exports.get(k) {
Some(a) => EntityType::internal_is_subtype_of(a, at, b, bt),
None => false,
})
}
}
#[derive(Debug, Clone)]
pub enum InstanceTypeKind {
Instantiated(TypeId),
Exports(IndexMap<String, EntityType>),
}
#[derive(Debug, Clone)]
pub struct InstanceType {
pub(crate) type_size: u32,
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].as_module_type().unwrap().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 {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
match (a, b) {
(Self::Module(a), Self::Module(b)) => ModuleType::internal_is_subtype_of(
at[*a].as_module_type().unwrap(),
at,
bt[*b].as_module_type().unwrap(),
bt,
),
(Self::Func(a), Self::Func(b)) => ComponentFuncType::internal_is_subtype_of(
at[*a].as_component_func_type().unwrap(),
at,
bt[*b].as_component_func_type().unwrap(),
bt,
),
(Self::Value(a), Self::Value(b)) => {
ComponentValType::internal_is_subtype_of(a, at, b, bt)
}
(Self::Type { referenced: a, .. }, Self::Type { referenced: b, .. }) => {
ComponentDefinedType::internal_is_subtype_of(
at[*a].as_defined_type().unwrap(),
at,
bt[*b].as_defined_type().unwrap(),
bt,
)
}
(Self::Instance(a), Self::Instance(b)) => {
ComponentInstanceType::internal_is_subtype_of(
at[*a].as_component_instance_type().unwrap(),
at,
bt[*b].as_component_instance_type().unwrap(),
bt,
)
}
(Self::Component(a), Self::Component(b)) => ComponentType::internal_is_subtype_of(
at[*a].as_component_type().unwrap(),
at,
bt[*b].as_component_type().unwrap(),
bt,
),
_ => false,
}
}
pub(crate) fn desc(&self) -> &'static str {
match self {
Self::Module(_) => "module",
Self::Func(_) => "function",
Self::Value(_) => "value",
Self::Type { .. } => "type",
Self::Instance(_) => "instance",
Self::Component(_) => "component",
}
}
pub(crate) fn type_size(&self) -> u32 {
match self {
Self::Module(ty)
| Self::Func(ty)
| Self::Type { referenced: ty, .. }
| Self::Instance(ty)
| Self::Component(ty) => ty.type_size,
Self::Value(ty) => ty.type_size(),
}
}
}
#[derive(Debug, Clone)]
pub struct ComponentType {
pub(crate) type_size: u32,
pub imports: IndexMap<KebabString, (Option<Url>, ComponentEntityType)>,
pub exports: IndexMap<KebabString, (Option<Url>, ComponentEntityType)>,
}
impl ComponentType {
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
a.imports.iter().all(|(k, (_, a))| match b.imports.get(k) {
Some((_, b)) => ComponentEntityType::internal_is_subtype_of(b, bt, a, at),
None => false,
}) && b.exports.iter().all(|(k, (_, b))| match a.exports.get(k) {
Some((_, a)) => ComponentEntityType::internal_is_subtype_of(a, at, b, bt),
None => false,
})
}
}
#[derive(Debug, Clone)]
pub enum ComponentInstanceTypeKind {
Defined(IndexMap<KebabString, (Option<Url>, ComponentEntityType)>),
Instantiated(TypeId),
Exports(IndexMap<KebabString, (Option<Url>, ComponentEntityType)>),
}
#[derive(Debug, Clone)]
pub struct ComponentInstanceType {
pub(crate) type_size: u32,
pub kind: ComponentInstanceTypeKind,
}
impl ComponentInstanceType {
pub fn exports<'a>(
&'a self,
types: TypesRef<'a>,
) -> impl ExactSizeIterator<Item = (&'a KebabStr, &'a Option<Url>, ComponentEntityType)> + Clone
{
self.internal_exports(types.list)
.iter()
.map(|(n, (u, t))| (n.as_kebab_str(), u, *t))
}
pub(crate) fn internal_exports<'a>(
&'a self,
types: &'a TypeList,
) -> &'a IndexMap<KebabString, (Option<Url>, ComponentEntityType)> {
match &self.kind {
ComponentInstanceTypeKind::Defined(exports)
| ComponentInstanceTypeKind::Exports(exports) => exports,
ComponentInstanceTypeKind::Instantiated(id) => {
&types[*id].as_component_type().unwrap().exports
}
}
}
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
let exports = a.internal_exports(at);
b.internal_exports(bt)
.iter()
.all(|(k, (_, b))| match exports.get(k) {
Some((_, a)) => ComponentEntityType::internal_is_subtype_of(a, at, b, bt),
None => false,
})
}
}
#[derive(Debug, Clone)]
pub struct ComponentFuncType {
pub(crate) type_size: u32,
pub params: Box<[(KebabString, ComponentValType)]>,
pub results: Box<[(Option<KebabString>, ComponentValType)]>,
}
impl ComponentFuncType {
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
if b.params.len() < a.params.len() {
return false;
}
if a.results.len() < b.results.len() {
return false;
}
for ((an, a), (bn, b)) in a.params.iter().zip(b.params.iter()) {
if an != bn {
return false;
}
if !ComponentValType::internal_is_subtype_of(b, bt, a, at) {
return false;
}
}
if !b
.params
.iter()
.skip(a.params.len())
.all(|(_, b)| b.is_optional(bt))
{
return false;
}
for ((an, a), (bn, b)) in a.results.iter().zip(b.results.iter()) {
if an != bn {
return false;
}
if !ComponentValType::internal_is_subtype_of(a, at, b, bt) {
return false;
}
}
true
}
pub(crate) fn lower(&self, types: &TypeList, import: bool) -> LoweringInfo {
let mut info = LoweringInfo::default();
for (_, ty) in self.params.iter() {
if !import && !info.requires_realloc {
info.requires_realloc = ty.requires_realloc(types);
}
if !ty.push_wasm_types(types, &mut info.params) {
info.params.clear();
assert!(info.params.push(ValType::I32));
info.requires_memory = true;
if !import {
info.requires_realloc = true;
}
break;
}
}
for (_, ty) in self.results.iter() {
if import && !info.requires_realloc {
info.requires_realloc = ty.requires_realloc(types);
}
if !ty.push_wasm_types(types, &mut info.results) {
info.results.clear();
if import {
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) type_size: u32,
pub fields: IndexMap<KebabString, ComponentValType>,
}
#[derive(Debug, Clone)]
pub struct VariantType {
pub(crate) type_size: u32,
pub cases: IndexMap<KebabString, VariantCase>,
}
#[derive(Debug, Clone)]
pub struct TupleType {
pub(crate) type_size: u32,
pub types: Box<[ComponentValType]>,
}
#[derive(Debug, Clone)]
pub struct UnionType {
pub(crate) type_size: u32,
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>),
Union(UnionType),
Option(ComponentValType),
Result {
ok: Option<ComponentValType>,
err: Option<ComponentValType>,
},
}
impl ComponentDefinedType {
pub(crate) fn requires_realloc(&self, types: &TypeList) -> bool {
match self {
Self::Primitive(ty) => ty.requires_realloc(),
Self::Record(r) => r.fields.values().any(|ty| ty.requires_realloc(types)),
Self::Variant(v) => v.cases.values().any(|case| {
case.ty
.map(|ty| ty.requires_realloc(types))
.unwrap_or(false)
}),
Self::List(_) => true,
Self::Tuple(t) => t.types.iter().any(|ty| ty.requires_realloc(types)),
Self::Union(u) => u.types.iter().any(|ty| ty.requires_realloc(types)),
Self::Flags(_) | Self::Enum(_) => false,
Self::Option(ty) => ty.requires_realloc(types),
Self::Result { ok, err } => {
ok.map(|ty| ty.requires_realloc(types)).unwrap_or(false)
|| err.map(|ty| ty.requires_realloc(types)).unwrap_or(false)
}
}
}
pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
Self::internal_is_subtype_of(a, at.list, b, bt.list)
}
pub(crate) fn internal_is_subtype_of(a: &Self, at: &TypeList, b: &Self, bt: &TypeList) -> bool {
match (a, b) {
(Self::Primitive(a), Self::Primitive(b)) => PrimitiveValType::is_subtype_of(*a, *b),
(Self::Record(a), Self::Record(b)) => {
for (name, a) in a.fields.iter() {
if let Some(b) = b.fields.get(name) {
if !ComponentValType::internal_is_subtype_of(a, at, b, bt) {
return false;
}
} else {
}
}
for (name, b) in b.fields.iter() {
if !b.is_optional(bt) && !a.fields.contains_key(name) {
return false;
}
}
true
}
(Self::Variant(a), Self::Variant(b)) => {
for (name, a) in a.cases.iter() {
if let Some(b) = b.cases.get(name) {
if !Self::is_optional_subtype_of(a.ty, at, b.ty, bt) {
return false;
}
} else if let Some(refines) = &a.refines {
if !b.cases.contains_key(refines) {
return false;
}
} else {
return false;
}
}
true
}
(Self::List(a), Self::List(b)) | (Self::Option(a), Self::Option(b)) => {
ComponentValType::internal_is_subtype_of(a, at, b, bt)
}
(Self::Tuple(a), Self::Tuple(b)) => {
if a.types.len() != b.types.len() {
return false;
}
a.types
.iter()
.zip(b.types.iter())
.all(|(a, b)| ComponentValType::internal_is_subtype_of(a, at, b, bt))
}
(Self::Union(a), Self::Union(b)) => {
if a.types.len() != b.types.len() {
return false;
}
a.types
.iter()
.zip(b.types.iter())
.all(|(a, b)| ComponentValType::internal_is_subtype_of(a, at, b, bt))
}
(Self::Flags(a), Self::Flags(b)) | (Self::Enum(a), Self::Enum(b)) => a.is_subset(b),
(Self::Result { ok: ao, err: ae }, Self::Result { ok: bo, err: be }) => {
Self::is_optional_subtype_of(*ao, at, *bo, bt)
&& Self::is_optional_subtype_of(*ae, at, *be, bt)
}
_ => false,
}
}
pub(crate) fn type_size(&self) -> u32 {
match self {
Self::Primitive(_) => 1,
Self::Flags(_) | Self::Enum(_) => 1,
Self::Record(r) => r.type_size,
Self::Variant(v) => v.type_size,
Self::Tuple(t) => t.type_size,
Self::Union(u) => u.type_size,
Self::List(ty) | Self::Option(ty) => ty.type_size(),
Self::Result { ok, err } => {
ok.map(|ty| ty.type_size()).unwrap_or(1) + err.map(|ty| ty.type_size()).unwrap_or(1)
}
}
}
fn is_optional_subtype_of(
a: Option<ComponentValType>,
at: &TypeList,
b: Option<ComponentValType>,
bt: &TypeList,
) -> bool {
match (a, b) {
(None, None) | (Some(_), None) => true,
(None, Some(_)) => false,
(Some(a), Some(b)) => ComponentValType::internal_is_subtype_of(&a, at, &b, bt),
}
}
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(_) => lowered_types.push(ValType::I32),
Self::Union(u) => Self::push_variant_wasm_types(u.types.iter(), types, lowered_types),
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"),
}
}
}
#[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),
}
}
fn types(&self, core: bool) -> Option<&'a [TypeId]> {
Some(match &self.kind {
TypesRefKind::Module(module) => {
if core {
&module.types
} else {
return None;
}
}
TypesRefKind::Component(component) => {
if core {
&component.core_types
} else {
&component.types
}
}
})
}
pub fn type_from_id(&self, id: TypeId) -> Option<&'a Type> {
self.list.get(id.index)
}
pub fn id_from_type_index(&self, index: u32, core: bool) -> Option<TypeId> {
self.types(core)?.get(index as usize).copied()
}
pub fn type_at(&self, index: u32, core: bool) -> Option<&'a Type> {
self.type_from_id(*self.types(core)?.get(index as usize)?)
}
pub fn func_type_at(&self, index: u32) -> Option<&'a FuncType> {
match self.type_at(index, true)? {
Type::Func(ty) => Some(ty),
_ => None,
}
}
pub fn table_at(&self, index: u32) -> Option<TableType> {
let tables = match &self.kind {
TypesRefKind::Module(module) => &module.tables,
TypesRefKind::Component(component) => &component.core_tables,
};
tables.get(index as usize).copied()
}
pub fn memory_at(&self, index: u32) -> Option<MemoryType> {
let memories = match &self.kind {
TypesRefKind::Module(module) => &module.memories,
TypesRefKind::Component(component) => &component.core_memories,
};
memories.get(index as usize).copied()
}
pub fn global_at(&self, index: u32) -> Option<GlobalType> {
let globals = match &self.kind {
TypesRefKind::Module(module) => &module.globals,
TypesRefKind::Component(component) => &component.core_globals,
};
globals.get(index as usize).copied()
}
pub fn tag_at(&self, index: u32) -> Option<&'a FuncType> {
let tags = match &self.kind {
TypesRefKind::Module(module) => &module.tags,
TypesRefKind::Component(component) => &component.core_tags,
};
Some(
self.list[*tags.get(index as usize)?]
.as_func_type()
.unwrap(),
)
}
pub fn function_at(&self, index: u32) -> Option<&'a FuncType> {
let id = match &self.kind {
TypesRefKind::Module(module) => {
&module.types[*module.functions.get(index as usize)? as usize]
}
TypesRefKind::Component(component) => component.core_funcs.get(index as usize)?,
};
match &self.list[*id] {
Type::Func(ty) => Some(ty),
_ => None,
}
}
pub fn element_at(&self, index: u32) -> Option<ValType> {
match &self.kind {
TypesRefKind::Module(module) => module.element_types.get(index as usize).copied(),
TypesRefKind::Component(_) => None,
}
}
pub fn component_function_at(&self, index: u32) -> Option<&'a ComponentFuncType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => Some(
self.list[*component.funcs.get(index as usize)?]
.as_component_func_type()
.unwrap(),
),
}
}
pub fn module_at(&self, index: u32) -> Option<&'a ModuleType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => Some(
self.list[*component.core_modules.get(index as usize)?]
.as_module_type()
.unwrap(),
),
}
}
pub fn instance_at(&self, index: u32) -> Option<&'a InstanceType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => {
let id = component.core_instances.get(index as usize)?;
match &self.list[*id] {
Type::Instance(ty) => Some(ty),
_ => None,
}
}
}
}
pub fn component_at(&self, index: u32) -> Option<&'a ComponentType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => Some(
self.list[*component.components.get(index as usize)?]
.as_component_type()
.unwrap(),
),
}
}
pub fn component_instance_at(&self, index: u32) -> Option<&'a ComponentInstanceType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => {
let id = component.instances.get(index as usize)?;
match &self.list[*id] {
Type::ComponentInstance(ty) => Some(ty),
_ => None,
}
}
}
}
pub fn value_at(&self, index: u32) -> Option<ComponentValType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => {
component.values.get(index as usize).map(|(r, _)| *r)
}
}
}
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_from_import(
&self,
import: &ComponentImport,
) -> Option<ComponentEntityType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => {
let key = KebabStr::new(import.name)?;
Some(component.imports.get(key)?.1)
}
}
}
pub fn component_entity_type_from_export(
&self,
export: &ComponentExport,
) -> Option<ComponentEntityType> {
match &self.kind {
TypesRefKind::Module(_) => None,
TypesRefKind::Component(component) => {
let key = KebabStr::new(export.name)?;
Some(component.exports.get(key)?.1)
}
}
}
}
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 type_from_id(&self, id: TypeId) -> Option<&Type> {
self.as_ref().type_from_id(id)
}
pub fn id_from_type_index(&self, index: u32, core: bool) -> Option<TypeId> {
self.as_ref().id_from_type_index(index, core)
}
pub fn type_at(&self, index: u32, core: bool) -> Option<&Type> {
self.as_ref().type_at(index, core)
}
pub fn func_type_at(&self, index: u32) -> Option<&FuncType> {
self.as_ref().func_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) -> Option<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) -> Option<MemoryType> {
self.as_ref().memory_at(index)
}
pub fn memory_count(&self) -> usize {
match &self.kind {
TypesKind::Module(module) => module.memories.len(),
TypesKind::Component(component) => component.core_memories.len(),
}
}
pub fn global_at(&self, index: u32) -> Option<GlobalType> {
self.as_ref().global_at(index)
}
pub fn global_count(&self) -> usize {
match &self.kind {
TypesKind::Module(module) => module.globals.len(),
TypesKind::Component(component) => component.core_globals.len(),
}
}
pub fn tag_at(&self, index: u32) -> Option<&FuncType> {
self.as_ref().tag_at(index)
}
pub fn tag_count(&self) -> usize {
match &self.kind {
TypesKind::Module(module) => module.tags.len(),
TypesKind::Component(component) => component.core_tags.len(),
}
}
pub fn function_at(&self, index: u32) -> Option<&FuncType> {
self.as_ref().function_at(index)
}
pub fn function_count(&self) -> usize {
match &self.kind {
TypesKind::Module(module) => module.functions.len(),
TypesKind::Component(component) => component.core_funcs.len(),
}
}
pub fn element_at(&self, index: u32) -> Option<ValType> {
self.as_ref().element_at(index)
}
pub fn element_count(&self) -> usize {
match &self.kind {
TypesKind::Module(module) => module.element_types.len(),
TypesKind::Component(_) => 0,
}
}
pub fn component_function_at(&self, index: u32) -> Option<&ComponentFuncType> {
self.as_ref().component_function_at(index)
}
pub fn component_function_count(&self) -> usize {
match &self.kind {
TypesKind::Module(_) => 0,
TypesKind::Component(component) => component.funcs.len(),
}
}
pub fn module_at(&self, index: u32) -> Option<&ModuleType> {
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) -> Option<&InstanceType> {
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) -> Option<&ComponentType> {
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) -> Option<&ComponentInstanceType> {
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) -> Option<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_from_import(
&self,
import: &ComponentImport,
) -> Option<ComponentEntityType> {
self.as_ref().component_entity_type_from_import(import)
}
pub fn component_entity_type_from_export(
&self,
export: &ComponentExport,
) -> Option<ComponentEntityType> {
self.as_ref().component_entity_type_from_export(export)
}
pub fn peel_alias(&self, ty: TypeId) -> Option<TypeId> {
self.list.peel_alias(ty)
}
}
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 get_mut(&mut self, index: usize) -> Option<&mut T> {
if index >= self.snapshots_total {
return self.cur.get_mut(index - self.snapshots_total);
}
panic!("cannot get a mutable reference in snapshotted part of list")
}
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> std::ops::Index<usize> for SnapshotList<T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &T {
self.get(index).unwrap()
}
}
impl<T> std::ops::IndexMut<usize> for SnapshotList<T> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut T {
self.get_mut(index).unwrap()
}
}
impl<T> std::ops::Index<TypeId> for SnapshotList<T> {
type Output = T;
#[inline]
fn index(&self, id: TypeId) -> &T {
self.get(id.index).unwrap()
}
}
impl<T> std::ops::IndexMut<TypeId> for SnapshotList<T> {
#[inline]
fn index_mut(&mut self, id: TypeId) -> &mut T {
self.get_mut(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,
}
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_anon(&mut self, ty: Type) -> TypeId {
let index = self.list.len();
let type_size = ty.type_size();
self.list.push(ty);
TypeId {
index,
type_size,
unique_id: 0,
}
}
pub fn push_defined(&mut self, ty: Type) -> TypeId {
let id = self.push_anon(ty);
self.with_unique(id)
}
}
impl Default for TypeAlloc {
fn default() -> TypeAlloc {
TypeAlloc {
list: Default::default(),
}
}
}