use crate::component::func::HostFunc;
use crate::component::linker::{Definition, NameMap, Strings};
use crate::types::matching;
use crate::Module;
use anyhow::{anyhow, bail, Context, Result};
use std::sync::Arc;
use wasmtime_environ::component::{
ComponentTypes, TypeComponentInstance, TypeDef, TypeFuncIndex, TypeModule,
};
pub struct TypeChecker<'a> {
pub types: &'a Arc<ComponentTypes>,
pub strings: &'a Strings,
}
impl TypeChecker<'_> {
pub fn definition(&self, expected: &TypeDef, actual: &Definition) -> Result<()> {
match *expected {
TypeDef::Module(t) => match actual {
Definition::Module(actual) => self.module(&self.types[t], actual),
_ => bail!("expected module found {}", actual.desc()),
},
TypeDef::ComponentInstance(t) => match actual {
Definition::Instance(actual) => self.instance(&self.types[t], actual),
_ => bail!("expected instance found {}", actual.desc()),
},
TypeDef::ComponentFunc(t) => match actual {
Definition::Func(actual) => self.func(t, actual),
_ => bail!("expected func found {}", actual.desc()),
},
TypeDef::Component(_) => bail!("expected component found {}", actual.desc()),
TypeDef::Interface(_) => bail!("expected type found {}", actual.desc()),
TypeDef::CoreFunc(_) => unreachable!(),
}
}
fn module(&self, expected: &TypeModule, actual: &Module) -> Result<()> {
let actual_types = actual.types();
let actual = actual.env_module();
for (name, expected) in expected.exports.iter() {
let idx = actual
.exports
.get(name)
.ok_or_else(|| anyhow!("module export `{name}` not defined"))?;
let actual = actual.type_of(*idx);
matching::entity_ty(expected, self.types.module_types(), &actual, actual_types)
.with_context(|| format!("module export `{name}` has the wrong type"))?;
}
for (module, name, actual) in actual.imports() {
let expected = expected
.imports
.get(&(module.to_string(), name.to_string()))
.ok_or_else(|| anyhow!("module import `{module}::{name}` not defined"))?;
matching::entity_ty(&actual, actual_types, expected, self.types.module_types())
.with_context(|| format!("module import `{module}::{name}` has the wrong type"))?;
}
Ok(())
}
fn instance(&self, expected: &TypeComponentInstance, actual: &NameMap) -> Result<()> {
for (name, expected) in expected.exports.iter() {
if let TypeDef::Interface(_) = expected {
continue;
}
let actual = self
.strings
.lookup(name)
.and_then(|name| actual.get(&name))
.ok_or_else(|| anyhow!("instance export `{name}` not defined"))?;
self.definition(expected, actual)
.with_context(|| format!("instance export `{name}` has the wrong type"))?;
}
Ok(())
}
fn func(&self, expected: TypeFuncIndex, actual: &HostFunc) -> Result<()> {
actual.typecheck(expected, self.types)
}
}
impl Definition {
fn desc(&self) -> &'static str {
match self {
Definition::Module(_) => "module",
Definition::Func(_) => "func",
Definition::Instance(_) => "instance",
}
}
}