use std::collections::hash_map::Entry;
use ahash::AHashMap;
use crate::{
ecmascript::{PrivateName, String, Value, builtins::ECMAScriptFunction, execution::Agent},
engine::{Bindable, NoGcScope, bindable_handle},
heap::{CompactionLists, ElementDescriptor, HeapMarkAndSweep, WorkQueues},
};
use super::PrivateEnvironment;
#[derive(Debug, Clone, Copy)]
pub(crate) enum PrivateMethod<'a> {
Getter(ECMAScriptFunction<'a>),
Setter(ECMAScriptFunction<'a>),
Method(ECMAScriptFunction<'a>),
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum PrivateField<'a> {
Field {
key: PrivateName,
},
Getter {
key: PrivateName,
get: ECMAScriptFunction<'a>,
},
Setter {
key: PrivateName,
set: ECMAScriptFunction<'a>,
},
Accessor {
key: PrivateName,
get: ECMAScriptFunction<'a>,
set: ECMAScriptFunction<'a>,
},
Method {
key: PrivateName,
method: ECMAScriptFunction<'a>,
},
}
impl<'a> PrivateField<'a> {
pub(crate) fn is_method(self) -> bool {
matches!(
self,
PrivateField::Getter { .. }
| PrivateField::Setter { .. }
| PrivateField::Accessor { .. }
| PrivateField::Method { .. }
)
}
pub(crate) fn into_element_descriptor(self) -> ElementDescriptor<'a> {
match self {
PrivateField::Getter { get, .. } => {
ElementDescriptor::ReadOnlyUnenumerableUnconfigurableAccessor { get: get.into() }
}
PrivateField::Setter { set, .. } => {
ElementDescriptor::WriteOnlyUnenumerableUnconfigurableAccessor { set: set.into() }
}
PrivateField::Accessor { get, set, .. } => {
ElementDescriptor::ReadWriteUnenumerableUnconfigurableAccessor {
get: get.into(),
set: set.into(),
}
}
PrivateField::Method { .. } => {
ElementDescriptor::ReadOnlyUnenumerableUnconfigurableData
}
_ => unreachable!(),
}
}
pub(crate) fn get_key(self) -> PrivateName {
match self {
PrivateField::Field { key }
| PrivateField::Getter { key, .. }
| PrivateField::Setter { key, .. }
| PrivateField::Accessor { key, .. }
| PrivateField::Method { key, .. } => key,
}
}
pub(crate) fn get_value(self) -> Option<Value<'a>> {
match self {
PrivateField::Field { .. }
| PrivateField::Getter { .. }
| PrivateField::Setter { .. }
| PrivateField::Accessor { .. } => None,
PrivateField::Method { method, .. } => Some(method.into()),
}
}
}
bindable_handle!(PrivateField);
#[derive(Debug)]
pub(crate) struct PrivateEnvironmentRecord {
outer_private_environment: Option<PrivateEnvironment<'static>>,
names: AHashMap<String<'static>, PrivateName>,
private_fields: Vec<PrivateField<'static>>,
first_private_name: PrivateName,
instance_private_field_count: u32,
instance_private_method_count: u32,
static_private_field_count: u32,
}
impl PrivateEnvironmentRecord {
fn add_private_name(&mut self, description: String) -> (PrivateName, bool) {
let current_number_of_names = self.names.len();
match self.names.entry(description.unbind()) {
Entry::Occupied(occupied_entry) => {
(*occupied_entry.get(), false)
}
Entry::Vacant(vacant_entry) => {
let next_private_name = PrivateName::from_u32(
self.first_private_name.into_u32() + current_number_of_names as u32,
);
vacant_entry.insert(next_private_name);
(next_private_name, true)
}
}
}
pub(crate) fn get_instance_private_fields<'gc>(
&self,
_: NoGcScope<'gc, '_>,
) -> &[PrivateField<'gc>] {
&self.private_fields
}
}
pub(crate) fn new_private_environment<'gc>(
agent: &mut Agent,
outer_private_environment: Option<PrivateEnvironment>,
private_names_count: usize,
gc: NoGcScope<'gc, '_>,
) -> PrivateEnvironment<'gc> {
let first_private_name = agent.create_private_names(private_names_count);
agent.heap.alloc_counter += core::mem::size_of::<Option<PrivateEnvironmentRecord>>();
let record = PrivateEnvironmentRecord {
outer_private_environment: outer_private_environment.unbind(),
names: AHashMap::with_capacity(private_names_count),
private_fields: vec![],
first_private_name,
instance_private_field_count: private_names_count as u32,
instance_private_method_count: 0,
static_private_field_count: 0,
};
agent.heap.environments.push_private_environment(record, gc)
}
pub(crate) fn resolve_private_identifier(
agent: &Agent,
private_env: PrivateEnvironment,
identifier: String,
) -> PrivateName {
let data = private_env.get_data(agent);
let names = &data.names;
if let Some(pn) = names.get(&identifier.unbind()) {
return *pn;
}
let outer_private_env = data
.outer_private_environment
.expect("outerPrivateEnv is null");
resolve_private_identifier(agent, outer_private_env, identifier)
}
impl<'e> PrivateEnvironment<'e> {
fn get_data(self, agent: &Agent) -> &PrivateEnvironmentRecord {
agent.heap.environments.get_private_environment(self)
}
fn get_data_mut(self, agent: &mut Agent) -> &mut PrivateEnvironmentRecord {
agent.heap.environments.get_private_environment_mut(self)
}
pub(crate) fn get_outer_env(self, agent: &Agent) -> Option<PrivateEnvironment<'e>> {
self.get_data(agent).outer_private_environment
}
pub(crate) unsafe fn get_private_name(self, agent: &Agent, offset: usize) -> PrivateName {
let data = self.get_data(agent);
if offset >= data.names.len() {
panic!("Attempted to get PrivateName outside the Private Environment's range");
}
PrivateName::from_u32(data.first_private_name.into_u32() + offset as u32)
}
pub(crate) fn resolve_description<'a>(
self,
agent: &Agent,
name: PrivateName,
gc: NoGcScope<'a, '_>,
) -> Option<String<'a>> {
let data = self.get_data(agent);
let inclusive_lower_bound = data.first_private_name.into_u32();
let exclusive_upper_bound = inclusive_lower_bound + data.names.len() as u32;
let name_value = name.into_u32();
if name_value >= exclusive_upper_bound {
return None;
} else if name_value < inclusive_lower_bound {
return data
.outer_private_environment?
.resolve_description(agent, name, gc);
}
for (description, private_name) in data.names.iter() {
if name == *private_name {
return Some(description.bind(gc));
}
}
unreachable!()
}
pub(crate) fn add_instance_private_field(self, agent: &mut Agent, description: String) {
let record = self.get_data_mut(agent);
let (private_name, added) = record.add_private_name(description);
assert!(added);
record
.private_fields
.push(PrivateField::Field { key: private_name });
}
pub(crate) fn add_instance_private_method(
self,
agent: &mut Agent,
description: String,
closure: PrivateMethod,
) {
let record = self.get_data_mut(agent);
let (private_name, added) = record.add_private_name(description);
if added {
let private_field = match closure {
PrivateMethod::Getter(f) => PrivateField::Getter {
key: private_name,
get: f,
},
PrivateMethod::Setter(f) => PrivateField::Setter {
key: private_name,
set: f,
},
PrivateMethod::Method(f) => PrivateField::Method {
key: private_name,
method: f,
},
};
record.private_fields.push(private_field.unbind());
record.instance_private_field_count -= 1;
record.instance_private_method_count += 1;
} else {
let existing_field = record
.private_fields
.iter_mut()
.find(|p| p.get_key() == private_name)
.expect("Didn't add PrivateName but couldn't find field definition");
match (*existing_field, closure) {
(PrivateField::Getter { key, get }, PrivateMethod::Setter(set))
| (PrivateField::Setter { key, set }, PrivateMethod::Getter(get)) => {
*existing_field = PrivateField::Accessor { key, get, set }.unbind();
}
_ => unreachable!(),
}
}
}
pub(crate) fn add_static_private_field(
self,
agent: &mut Agent,
description: String,
) -> PrivateName {
let record = self.get_data_mut(agent);
let (private_name, added) = record.add_private_name(description);
assert!(added);
record.instance_private_field_count -= 1;
record.static_private_field_count += 1;
private_name
}
pub(crate) fn add_static_private_method(
self,
agent: &mut Agent,
description: String,
) -> PrivateName {
let record = self.get_data_mut(agent);
let (private_name, added) = record.add_private_name(description);
if added {
record.instance_private_field_count -= 1;
}
private_name
}
}
impl HeapMarkAndSweep for PrivateEnvironment<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
queues.private_environments.push(*self);
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
compactions.private_environments.shift_index(&mut self.0);
}
}
impl HeapMarkAndSweep for PrivateField<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
match self {
Self::Field { .. } => {}
PrivateField::Accessor { get, set, .. } => {
get.mark_values(queues);
set.mark_values(queues);
}
PrivateField::Getter { get: f, .. }
| PrivateField::Setter { set: f, .. }
| PrivateField::Method { method: f, .. } => f.mark_values(queues),
}
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
match self {
Self::Field { .. } => {}
PrivateField::Accessor { get, set, .. } => {
get.sweep_values(compactions);
set.sweep_values(compactions);
}
PrivateField::Getter { get: f, .. }
| PrivateField::Setter { set: f, .. }
| PrivateField::Method { method: f, .. } => f.sweep_values(compactions),
}
}
}
impl HeapMarkAndSweep for PrivateEnvironmentRecord {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self {
outer_private_environment,
names,
private_fields: private_methods,
first_private_name: _,
instance_private_field_count: _,
instance_private_method_count: _,
static_private_field_count: _,
} = self;
outer_private_environment.mark_values(queues);
for key in names.keys() {
key.mark_values(queues);
}
for func in private_methods {
func.mark_values(queues);
}
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self {
outer_private_environment,
names,
private_fields: private_methods,
first_private_name: _,
instance_private_field_count: _,
instance_private_method_count: _,
static_private_field_count: _,
} = self;
outer_private_environment.sweep_values(compactions);
for func in private_methods {
func.sweep_values(compactions);
}
let mut replacements = Vec::new();
for (key, _) in names.iter_mut() {
if let String::String(old_key) = key {
let old_key = *old_key;
let mut new_key = old_key;
new_key.sweep_values(compactions);
if old_key != new_key {
replacements.push((old_key, new_key));
}
}
}
replacements.sort();
for (old_key, new_key) in replacements.into_iter() {
let binding = names.remove(&old_key.into()).unwrap();
let did_insert = names.insert(new_key.into(), binding).is_none();
assert!(did_insert, "Failed to insert PrivateName {new_key:#?}");
}
}
}