use crate::*;
#[derive(Default)]
pub struct CloneMaps {
pub(super) types: HashMap<TypeId, TypeId>,
pub(super) interfaces: HashMap<InterfaceId, InterfaceId>,
}
impl CloneMaps {
pub fn types(&self) -> &HashMap<TypeId, TypeId> {
&self.types
}
pub fn interfaces(&self) -> &HashMap<InterfaceId, InterfaceId> {
&self.interfaces
}
}
pub struct Cloner<'a> {
pub resolve: &'a mut Resolve,
pub maps: &'a mut CloneMaps,
prev_owner: TypeOwner,
new_owner: TypeOwner,
pub new_package: Option<PackageId>,
}
impl<'a> Cloner<'a> {
pub fn new(
resolve: &'a mut Resolve,
maps: &'a mut CloneMaps,
prev_owner: TypeOwner,
new_owner: TypeOwner,
) -> Cloner<'a> {
Cloner {
prev_owner,
new_owner,
resolve,
maps,
new_package: None,
}
}
pub fn register_world_type_overlap(&mut self, from: WorldId, into: WorldId) {
let into = &self.resolve.worlds[into];
let from = &self.resolve.worlds[from];
for (name, into_import) in into.imports.iter() {
let WorldKey::Name(_) = name else { continue };
let WorldItem::Type { id: into_id, .. } = into_import else {
continue;
};
let Some(WorldItem::Type { id: from_id, .. }) = from.imports.get(name) else {
continue;
};
self.maps.types.insert(*from_id, *into_id);
}
}
pub fn world_item(&mut self, key: &WorldKey, item: &mut WorldItem) {
match key {
WorldKey::Name(_) => {}
WorldKey::Interface(_) => return,
}
match item {
WorldItem::Type { id, .. } => {
self.type_id(id);
}
WorldItem::Function(f) => {
self.function(f);
}
WorldItem::Interface { id, .. } => {
self.interface(id);
}
}
}
fn type_id(&mut self, ty: &mut TypeId) {
if !self.maps.types.contains_key(ty) {
let mut new = self.resolve.types[*ty].clone();
self.type_def(&mut new);
let id = self.resolve.types.alloc(new);
self.maps.types.insert(*ty, id);
}
*ty = self.maps.types[&*ty];
}
fn type_def(&mut self, def: &mut TypeDef) {
if def.owner != TypeOwner::None {
assert_eq!(def.owner, self.prev_owner);
def.owner = self.new_owner;
}
match &mut def.kind {
TypeDefKind::Type(Type::Id(id)) => {
if self.resolve.types[*id].owner == self.prev_owner {
self.type_id(id);
} else if let Some(new_id) = self.maps.types.get(id) {
*id = *new_id;
} else {
}
}
TypeDefKind::Type(_)
| TypeDefKind::Resource
| TypeDefKind::Flags(_)
| TypeDefKind::Enum(_) => {}
TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)) => {
self.type_id(ty);
}
TypeDefKind::Option(ty)
| TypeDefKind::List(ty)
| TypeDefKind::FixedLengthList(ty, ..) => {
self.ty(ty);
}
TypeDefKind::Map(k, v) => {
self.ty(k);
self.ty(v);
}
TypeDefKind::Tuple(list) => {
for ty in list.types.iter_mut() {
self.ty(ty);
}
}
TypeDefKind::Record(r) => {
for field in r.fields.iter_mut() {
self.ty(&mut field.ty);
}
}
TypeDefKind::Variant(r) => {
for case in r.cases.iter_mut() {
if let Some(ty) = &mut case.ty {
self.ty(ty);
}
}
}
TypeDefKind::Result(r) => {
if let Some(ok) = &mut r.ok {
self.ty(ok);
}
if let Some(err) = &mut r.err {
self.ty(err);
}
}
TypeDefKind::Future(ty) | TypeDefKind::Stream(ty) => {
if let Some(ty) = ty {
self.ty(ty);
}
}
TypeDefKind::Unknown => {}
}
}
fn ty(&mut self, ty: &mut Type) {
match ty {
Type::Id(id) => self.type_id(id),
_ => {}
}
}
fn function(&mut self, func: &mut Function) {
if let Some(id) = func.kind.resource_mut() {
self.type_id(id);
}
for param in func.params.iter_mut() {
self.ty(&mut param.ty);
}
if let Some(ty) = &mut func.result {
self.ty(ty);
}
}
pub fn interface(&mut self, id: &mut InterfaceId) {
let old_id = *id;
let mut new = self.resolve.interfaces[old_id].clone();
let next_id = self.resolve.interfaces.next_id();
let mut clone = Cloner::new(
self.resolve,
self.maps,
TypeOwner::Interface(old_id),
TypeOwner::Interface(next_id),
);
for id in new.types.values_mut() {
clone.type_id(id);
}
for func in new.functions.values_mut() {
clone.function(func);
}
new.package = Some(self.new_package.unwrap_or_else(|| match self.new_owner {
TypeOwner::Interface(id) => self.resolve.interfaces[id].package.unwrap(),
TypeOwner::World(id) => self.resolve.worlds[id].package.unwrap(),
TypeOwner::None => unreachable!(),
}));
new.clone_of = Some(old_id);
*id = self.resolve.interfaces.alloc(new);
let prev = self.maps.interfaces.insert(old_id, next_id);
assert!(prev.is_none());
assert_eq!(*id, next_id);
}
}