use crate::ns::*;
pub struct MethodOverriding<'a>(pub &'a SemanticHost);
impl<'a> MethodOverriding<'a> {
pub fn abstract_methods_not_overriden(&mut self, class: &Thingy) -> Result<Vec<Thingy>, DeferError> {
let base_class = class.extends_class(self.0);
if base_class.is_none() {
return Ok(vec![]);
}
let base_class = base_class.unwrap();
if base_class.is::<UnresolvedThingy>() {
return Err(DeferError());
}
let mut r: Vec<Thingy> = vec![];
for (name, prop) in base_class.prototype(self.0).borrow().iter() {
if prop.is::<MethodSlot>() {
if prop.is_abstract() {
let prop2 = if name.namespace().is::<SystemNamespace>() {
class.prototype(self.0).get_in_system_ns_kind(name.namespace().system_ns_kind().unwrap(), &name.local_name()).ok().unwrap_or(None)
} else {
class.prototype(self.0).get(name)
};
if prop2.is_none() || !prop2.unwrap().is::<MethodSlot>() {
r.push(prop.clone());
}
}
}
else if prop.is::<VirtualSlot>() {
if let Some(getter) = prop.getter(self.0) {
if prop.not_overriden_abstract_getter(&getter, class, self.0) {
r.push(getter.clone());
}
}
if let Some(setter) = prop.setter(self.0) {
if prop.not_overriden_abstract_setter(&setter, class, self.0) {
r.push(setter.clone());
}
}
}
}
Ok(r)
}
pub fn override_method(&mut self, method: &Thingy) -> Result<(), MethodOverridingError> {
let name = method.name();
let class = method.parent().unwrap();
assert!(class.is::<ClassType>() || class.is::<EnumType>());
let base_type = class.extends_class(self.0);
if base_type.is_none() {
return Err(MethodOverridingError::MustOverrideAMethod);
}
let base_type = base_type.unwrap();
let base_method = self.lookup_method(&name, &base_type)?;
if base_method.is_none() {
return Err(MethodOverridingError::MustOverrideAMethod);
}
let mut base_method = base_method.unwrap();
let virtual_property = method.of_virtual_slot(self.0);
if let Some(virtual_property) = virtual_property {
let is_getter = Some(method.clone()) == virtual_property.getter(self.0);
if is_getter {
if !(base_method.is::<VirtualSlot>() && base_method.getter(self.0).is_some()) {
return Err(MethodOverridingError::MustOverrideAMethod);
}
base_method = base_method.getter(self.0).unwrap();
} else {
if !(base_method.is::<VirtualSlot>() && base_method.setter(self.0).is_some()) {
return Err(MethodOverridingError::MustOverrideAMethod);
}
base_method = base_method.setter(self.0).unwrap();
}
} else if !base_method.is::<MethodSlot>() {
return Err(MethodOverridingError::MustOverrideAMethod);
}
let base_signature = base_method.signature(self.0);
base_signature.defer().map_err(|_| MethodOverridingError::Defer)?;
let subtype_signature = method.signature(self.0);
subtype_signature.defer().map_err(|_| MethodOverridingError::Defer)?;
if !self.compatible_override(&base_signature, &subtype_signature) {
return Err(MethodOverridingError::IncompatibleOverride {
expected_signature: base_signature,
actual_signature: subtype_signature,
});
}
if base_method.is_final() {
return Err(MethodOverridingError::OverridingFinalMethod);
}
base_method.overriden_by(self.0).push(method.clone());
method.set_overrides_method(Some(base_method));
Ok(())
}
fn lookup_method(&mut self, name: &QName, base_type: &Thingy) -> Result<Option<Thingy>, MethodOverridingError> {
for class in base_type.descending_class_hierarchy(self.0).collect::<Vec<_>>() {
class.defer().map_err(|_| MethodOverridingError::Defer)?;
let prop = if name.namespace().is::<SystemNamespace>() {
class.prototype(self.0).get_in_system_ns_kind(name.namespace().system_ns_kind().unwrap(), &name.local_name()).ok().unwrap_or(None)
} else {
class.prototype(self.0).get(name)
};
if let Some(prop) = prop {
prop.property_static_type(self.0).defer().map_err(|_| MethodOverridingError::Defer)?;
if prop.is::<VirtualSlot>() {
if let Some(getter) = prop.getter(self.0) {
getter.signature(self.0).defer().map_err(|_| MethodOverridingError::Defer)?;
}
if let Some(setter) = prop.setter(self.0) {
setter.signature(self.0).defer().map_err(|_| MethodOverridingError::Defer)?;
}
}
return Ok(Some(prop));
}
}
Ok(None)
}
fn compatible_override(&mut self, base_signature: &Thingy, subtype_signature: &Thingy) -> bool {
base_signature == subtype_signature
}
}