use super::*;
use std::cell::RefCell;
use rustc_hash::{FxBuildHasher, FxHashSet};
pub(super) type FxIndexMap<K, V> = IndexMap<K, V, FxBuildHasher>;
pub(super) type Name = Arc<str>;
#[derive(Clone)]
pub(super) struct ObjectTypeMetadata {
pub(super) name: String,
pub(super) identity: Option<String>,
pub(super) parent_names: Vec<String>,
pub(super) parent_identities: Vec<String>,
}
pub(super) fn object_type_metadata(source: &ObjectSource) -> Option<ObjectTypeMetadata> {
Some(ObjectTypeMetadata {
name: source.type_name.clone()?,
identity: source.type_identity.clone(),
parent_names: source.parent_type_names.clone(),
parent_identities: source.parent_type_identities.clone(),
})
}
#[derive(Debug, Default, Clone)]
pub(super) struct Scope {
pub(super) vars: Rc<FxIndexMap<Name, Value>>,
pub(super) type_aliases: Rc<FxIndexMap<Name, crate::parser::TypeExpr>>,
pub(super) module_identities: Rc<FxIndexMap<Name, String>>,
pub(super) poisoned: Rc<FxIndexMap<Name, String>>,
pub(super) declared: Rc<FxHashSet<Name>>,
pub(super) this_aliases: Rc<FxHashSet<String>>,
pub(super) type_namespace: Option<String>,
pub(super) receiver_entries: Option<Arc<Vec<Entry>>>,
pub(super) receiver_list_base: Option<usize>,
pub(super) parent: Option<Rc<Scope>>,
}
impl Scope {
pub(super) fn child(&self) -> Self {
Self {
vars: Rc::default(),
type_aliases: Rc::default(),
module_identities: Rc::default(),
poisoned: Rc::default(),
declared: Rc::default(),
this_aliases: Rc::default(),
type_namespace: self.type_namespace.clone(),
receiver_entries: self.receiver_entries.clone(),
receiver_list_base: self.receiver_list_base,
parent: Some(Rc::new(self.clone())),
}
}
pub(super) fn runtime_type_identity(&self, name: &str) -> String {
self.type_namespace
.as_ref()
.map(|namespace| format!("{namespace}.{name}"))
.unwrap_or_else(|| name.to_string())
}
pub(super) fn set(&mut self, name: String, val: Value) {
self.set_name(name.into(), val);
}
pub(super) fn set_name(&mut self, name: Name, val: Value) {
if self.poisoned.contains_key(&*name) {
Rc::make_mut(&mut self.poisoned).shift_remove(&*name);
}
if self.module_identities.contains_key(&*name) {
Rc::make_mut(&mut self.module_identities).shift_remove(&*name);
}
if self.this_aliases.contains(&*name) {
Rc::make_mut(&mut self.this_aliases).remove(&*name);
}
Rc::make_mut(&mut self.vars).insert(name, val);
}
pub(super) fn mark_this_alias(&mut self, name: &str) {
if !self.this_aliases.contains(name) {
Rc::make_mut(&mut self.this_aliases).insert(name.to_string());
}
}
pub(super) fn visible_this_aliases(&self) -> Vec<String> {
let mut names = Vec::new();
let mut seen = FxHashSet::default();
let mut inner: Vec<&Scope> = Vec::new();
let mut level = Some(self);
while let Some(scope) = level {
for name in scope.this_aliases.iter() {
if scope.binds(name)
&& seen.insert(name.as_str())
&& !inner.iter().any(|inner| inner.binds(name))
{
names.push(name.clone());
}
}
inner.push(scope);
level = scope.parent.as_deref();
}
names
}
fn binds(&self, name: &str) -> bool {
self.vars.contains_key(name) || self.poisoned.contains_key(name)
}
pub(super) fn declare(&mut self, name: String, val: Value) {
self.declare_name(name.into(), val);
}
pub(super) fn declare_name(&mut self, name: Name, val: Value) {
if !self.declared.contains(&*name) {
Rc::make_mut(&mut self.declared).insert(name.clone());
}
self.set_name(name, val);
}
pub(super) fn declare_poisoned(&mut self, name: String, message: String) {
let name = Name::from(name);
if !self.declared.contains(&*name) {
Rc::make_mut(&mut self.declared).insert(name.clone());
}
Rc::make_mut(&mut self.poisoned).insert(name, message);
}
pub(super) fn is_declared(&self, name: &str) -> bool {
if self.vars.contains_key(name) || self.poisoned.contains_key(name) {
self.declared.contains(name)
} else {
self.parent
.as_ref()
.is_some_and(|parent| parent.is_declared(name))
}
}
pub(super) fn flatten_declared(&self) -> NameSet {
let mut declared = self
.parent
.as_ref()
.map(|parent| parent.flatten_declared())
.unwrap_or_default();
for name in self.vars.keys().chain(self.poisoned.keys()) {
if self.declared.contains(name) {
declared.insert(name.clone());
} else {
declared.remove(name);
}
}
declared
}
pub(super) fn set_module_identity(&mut self, name: String, identity: String) {
Rc::make_mut(&mut self.module_identities).insert(name.into(), identity);
}
pub(super) fn module_identity(&self, name: &str) -> Option<&String> {
if self.vars.contains_key(name) {
self.module_identities.get(name)
} else {
self.parent.as_ref()?.module_identity(name)
}
}
pub(super) fn flatten_module_identities(&self) -> IndexMap<String, String> {
let mut identities = self
.parent
.as_ref()
.map(|parent| parent.flatten_module_identities())
.unwrap_or_default();
for name in self.vars.keys() {
identities.shift_remove(&**name);
}
identities.extend(
self.module_identities
.iter()
.map(|(k, v)| (k.to_string(), v.clone())),
);
identities
}
pub(super) fn poison(&mut self, name: String, message: String) {
Rc::make_mut(&mut self.poisoned).insert(name.into(), message);
}
pub(super) fn poison_of(&self, name: &str) -> Option<&String> {
self.poisoned.get(name).or_else(|| {
self.parent
.as_ref()
.and_then(|parent| parent.poison_of(name))
})
}
pub(super) fn set_type_alias(&mut self, name: String, ty: crate::parser::TypeExpr) {
Rc::make_mut(&mut self.type_aliases).insert(name.into(), ty);
}
pub(super) fn get_type_alias(&self, name: &str) -> Option<&crate::parser::TypeExpr> {
self.type_aliases
.get(name)
.or_else(|| self.parent.as_ref().and_then(|p| p.get_type_alias(name)))
}
pub(super) fn get(&self, name: &str) -> Option<&Value> {
if self.poisoned.contains_key(name) {
return None;
}
self.vars
.get(name)
.or_else(|| self.parent.as_ref().and_then(|p| p.get(name)))
}
pub(super) fn flatten(&self) -> ScopeMap {
let mut result = self
.parent
.as_ref()
.map(|p| p.flatten())
.unwrap_or_default();
for name in self.poisoned.keys() {
result.shift_remove(&**name);
}
result.extend(self.vars.iter().map(|(k, v)| (k.clone(), v.clone())));
result
}
pub(super) fn flatten_names<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> ScopeMap {
let mut names = names.into_iter().collect::<Vec<_>>();
names.sort_unstable();
names.dedup();
names
.into_iter()
.filter_map(|name| {
let (key, value) = self.flattened(name)?;
Some((key.clone(), value.clone()))
})
.collect()
}
fn flattened(&self, name: &str) -> Option<(&Name, &Value)> {
if let Some(binding) = self.vars.get_key_value(name) {
return Some(binding);
}
if self.poisoned.contains_key(name) {
return None;
}
self.parent.as_ref()?.flattened(name)
}
pub(super) fn flatten_type_aliases(&self) -> IndexMap<String, crate::parser::TypeExpr> {
let mut result = self
.parent
.as_ref()
.map(|p| p.flatten_type_aliases())
.unwrap_or_default();
result.extend(
self.type_aliases
.iter()
.map(|(k, v)| (k.to_string(), v.clone())),
);
result
}
}
pub(super) fn capture_scope(scope: &Scope) -> CapturedScope {
CapturedScope {
values: scope.flatten(),
declared: scope.flatten_declared(),
body_members: HashSet::new(),
module_identities: scope.flatten_module_identities(),
type_aliases: scope.flatten_type_aliases(),
type_namespace: scope.type_namespace.clone(),
}
}
pub(super) fn capture_object_source_scope(source: &ObjectSource) -> CapturedScope {
let type_namespace = source
.type_name
.as_deref()
.zip(source.type_identity.as_deref())
.and_then(|(name, identity)| identity.strip_suffix(&format!(".{name}")))
.map(str::to_owned);
CapturedScope {
values: source.scope.clone(),
declared: source.scope_declared.clone(),
body_members: source.body_members.clone(),
module_identities: source.scope_module_identities.clone(),
type_aliases: source.scope_type_aliases.clone(),
type_namespace,
}
}
pub(super) fn restore_scope(captured: &CapturedScope) -> Scope {
let mut scope = Scope {
type_namespace: captured.type_namespace.clone(),
..Scope::default()
};
for (name, value) in &captured.values {
scope.set_name(name.clone(), value.clone());
}
scope.declared = Rc::new(captured.declared.clone());
for (name, identity) in &captured.module_identities {
scope.set_module_identity(name.clone(), identity.clone());
}
for (name, ty) in &captured.type_aliases {
scope.set_type_alias(name.clone(), ty.clone());
}
scope
}
pub(super) fn scope_for_object_entry(
entry_index: usize,
object: &Scope,
entry_scopes: Option<&[Option<Arc<CapturedScope>>]>,
entry_owners: &EntryOwners,
own_body: Option<(&Scope, &HashSet<String>)>,
) -> Scope {
if let Some(captured) = entry_scopes
.and_then(|scopes| scopes.get(entry_index))
.and_then(Option::as_ref)
{
let lexical = entry_owners.restored(captured);
let none = HashSet::new();
let owned = entry_owners
.owners
.get(entry_index)
.map_or(&none, Rc::as_ref);
return entry_owners.bindings(Arc::as_ptr(captured) as usize, &lexical, object, owned);
}
match own_body {
Some((definition, owned)) => entry_owners.bindings(
definition as *const Scope as usize,
definition,
object,
owned,
),
None => object.clone(),
}
}
#[derive(Default)]
pub(super) struct EntryOwners {
owners: Vec<Rc<HashSet<String>>>,
restored: RefCell<HashMap<*const CapturedScope, Scope>>,
bindings: RefCell<HashMap<usize, ObjectBindings>>,
}
struct ObjectBindings {
scope: Scope,
hidden: FxHashSet<Name>,
}
impl EntryOwners {
fn bindings(
&self,
key: usize,
lexical: &Scope,
object: &Scope,
owned: &HashSet<String>,
) -> Scope {
let mut cache = self.bindings.borrow_mut();
let cached = cache.entry(key).or_insert_with(|| ObjectBindings {
scope: lexical.child(),
hidden: FxHashSet::default(),
});
update_object_bindings(cached, lexical, object, owned);
cached.scope.clone()
}
pub(super) fn release_this(&self, aliases: &[String]) {
for cached in self.bindings.borrow_mut().values_mut() {
if Rc::strong_count(&cached.scope.vars) == 1 {
release_this_aliases(&mut cached.scope, aliases);
}
}
}
fn restored(&self, captured: &Arc<CapturedScope>) -> Scope {
self.restored
.borrow_mut()
.entry(Arc::as_ptr(captured))
.or_insert_with(|| restore_scope(captured))
.clone()
}
}
pub(super) fn entry_scope_owners(
entries: &[Entry],
entry_scopes: Option<&[Option<Arc<CapturedScope>>]>,
inherited: Option<&ObjectSource>,
) -> EntryOwners {
let Some(entry_scopes) = entry_scopes else {
return EntryOwners::default();
};
let inherited_entries = inherited
.map(|source| source.entries.iter().zip(&source.entry_scopes))
.into_iter()
.flatten();
let mut groups: HashMap<*const CapturedScope, HashSet<String>> = HashMap::new();
for (entry, captured) in entries.iter().zip(entry_scopes).chain(inherited_entries) {
let Some(captured) = captured else {
continue;
};
let names = groups.entry(Arc::as_ptr(captured)).or_insert_with(|| {
captured.body_members.clone()
});
if let Some(name) = entry_member_name(entry) {
names.insert(name.clone());
}
}
let groups = groups
.into_iter()
.map(|(ptr, names)| (ptr, Rc::new(names)))
.collect::<HashMap<_, _>>();
let owners = entry_scopes
.iter()
.map(|captured| {
captured
.as_ref()
.and_then(|captured| groups.get(&Arc::as_ptr(captured)))
.cloned()
.unwrap_or_default()
})
.collect();
EntryOwners {
owners,
restored: RefCell::default(),
bindings: RefCell::default(),
}
}
pub(super) fn entry_member_name(entry: &Entry) -> Option<&String> {
match entry {
Entry::Property(prop) => Some(&prop.name),
Entry::ClassDef(name, ..) | Entry::TypeAlias(name, _) => Some(name),
_ => None,
}
}
pub(super) fn own_body_names(
entries: &[Entry],
entry_scopes: Option<&[Option<Arc<CapturedScope>>]>,
inherited: Option<&ObjectSource>,
) -> Option<HashSet<String>> {
let entry_scopes = entry_scopes?;
let own = |entries: &[Entry], scopes: &[Option<Arc<CapturedScope>>]| {
entries
.iter()
.enumerate()
.filter(|(index, _)| scopes.get(*index).is_none_or(Option::is_none))
.filter_map(|(_, entry)| entry_member_name(entry).cloned())
.collect::<Vec<_>>()
};
let mut names: HashSet<String> = own(entries, entry_scopes).into_iter().collect();
if let Some(source) = inherited {
names.extend(own(&source.entries, &source.entry_scopes));
names.extend(source.body_members.iter().cloned());
}
Some(names)
}
fn update_object_bindings(
bindings: &mut ObjectBindings,
lexical: &Scope,
object: &Scope,
owned: &HashSet<String>,
) {
let ObjectBindings { scope, hidden } = bindings;
scope.receiver_entries = object.receiver_entries.clone();
scope.receiver_list_base = object.receiver_list_base;
for (name, value) in object.vars.iter() {
if &**name == "super" || hidden.contains(&**name) {
continue;
}
if scope
.vars
.get(&**name)
.is_some_and(|bound| same_value(bound, value))
{
continue;
}
if owned.contains(&**name) {
if !scope.declared.contains(&**name) {
Rc::make_mut(&mut scope.declared).insert(name.clone());
}
} else if !scope.vars.contains_key(&**name) && lexical.is_declared(name) {
hidden.insert(name.clone());
continue;
}
Rc::make_mut(&mut scope.vars).insert(name.clone(), value.clone());
}
if !object.poisoned.is_empty() || !scope.poisoned.is_empty() {
let mut poisoned = FxIndexMap::default();
for (name, message) in object.poisoned.iter() {
if owned.contains(&**name) {
if !scope.declared.contains(&**name) {
Rc::make_mut(&mut scope.declared).insert(name.clone());
}
poisoned.insert(name.clone(), message.clone());
} else if !lexical.is_declared(name) {
poisoned.insert(name.clone(), message.clone());
}
}
scope.poisoned = Rc::new(poisoned);
}
scope.this_aliases = object.this_aliases.clone();
scope.type_aliases = object.type_aliases.clone();
scope.module_identities = object.module_identities.clone();
}
fn same_value(a: &Value, b: &Value) -> bool {
match (a, b) {
(Value::Null, Value::Null) => true,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Int(a), Value::Int(b)) => a == b,
(Value::Float(a), Value::Float(b)) => a.to_bits() == b.to_bits(),
(Value::String(a), Value::String(b)) => a == b,
(Value::Object(a, a_src), Value::Object(b, b_src)) => {
Arc::ptr_eq(a, b)
&& match (a_src, b_src) {
(Some(a), Some(b)) => Arc::ptr_eq(a, b),
(None, None) => true,
_ => false,
}
}
(Value::List(a), Value::List(b)) => {
a.len() == b.len() && a.iter().zip(b).all(|(a, b)| same_value(a, b))
}
(
Value::Lambda(a_params, a_body, a_captured),
Value::Lambda(b_params, b_body, b_captured),
) => {
Arc::ptr_eq(a_params, b_params)
&& Arc::ptr_eq(a_body, b_body)
&& Arc::ptr_eq(a_captured, b_captured)
}
_ => false,
}
}
pub(super) fn mapping_amendment_scopes(
captured: &ScopeMap,
captured_declared: &NameSet,
current: &Scope,
) -> (Scope, Scope) {
let current_declared = current.flatten_declared();
let mut inherited = Scope::default();
for (key, value) in captured {
if captured_declared.contains(&**key) {
inherited.declare_name(key.clone(), value.clone());
} else {
inherited.set_name(key.clone(), value.clone());
}
}
for (key, value) in current.flatten() {
if inherited.get(&key).is_none() {
if current_declared.contains(&*key) {
inherited.declare_name(key, value);
} else {
inherited.set_name(key, value);
}
}
}
for (key, ty) in current.flatten_type_aliases() {
inherited.set_type_alias(key, ty);
}
let mut amendment = inherited.clone();
for (key, value) in current.flatten() {
if current_declared.contains(&*key) {
amendment.declare_name(key, value);
} else {
Rc::make_mut(&mut amendment.declared).remove(&*key);
amendment.set_name(key, value);
}
}
for (key, ty) in current.flatten_type_aliases() {
amendment.set_type_alias(key, ty);
}
(inherited, amendment)
}