use oxc_ast::ast::*;
use oxc_data_structures::stack::NonEmptyStack;
use oxc_traverse::BoundIdentifier;
use super::{ClassBindings, ClassProperties, IdentIndexMap};
pub(super) struct ClassDetails<'a> {
pub is_declaration: bool,
pub is_transform_required: bool,
pub private_props: Option<IdentIndexMap<'a, PrivateProp<'a>>>,
pub bindings: ClassBindings<'a>,
}
impl ClassDetails<'_> {
pub fn dummy(is_declaration: bool) -> Self {
Self {
is_declaration,
is_transform_required: false,
private_props: None,
bindings: ClassBindings::dummy(),
}
}
}
pub(super) struct PrivateProp<'a> {
pub binding: BoundIdentifier<'a>,
pub is_static: bool,
pub method_kind: Option<MethodDefinitionKind>,
pub is_accessor: bool,
pub binding2: Option<BoundIdentifier<'a>>,
}
impl<'a> PrivateProp<'a> {
pub fn new(
binding: BoundIdentifier<'a>,
is_static: bool,
method_kind: Option<MethodDefinitionKind>,
is_accessor: bool,
) -> Self {
Self { binding, is_static, method_kind, is_accessor, binding2: None }
}
pub fn is_method(&self) -> bool {
self.method_kind.is_some()
}
pub fn is_accessor(&self) -> bool {
self.is_accessor || self.method_kind.is_some_and(MethodDefinitionKind::is_accessor)
}
pub fn set_binding2(&mut self, binding: BoundIdentifier<'a>) {
self.binding2 = Some(binding);
}
}
pub(super) struct ClassesStack<'a> {
stack: NonEmptyStack<ClassDetails<'a>>,
}
impl<'a> ClassesStack<'a> {
pub fn new() -> Self {
Self { stack: NonEmptyStack::new(ClassDetails::dummy(false)) }
}
#[inline]
pub fn push(&mut self, class: ClassDetails<'a>) {
self.stack.push(class);
}
#[inline]
pub fn pop(&mut self) -> ClassDetails<'a> {
self.stack.pop()
}
#[inline]
pub fn last(&self) -> &ClassDetails<'a> {
self.stack.last()
}
#[inline]
pub fn last_mut(&mut self) -> &mut ClassDetails<'a> {
self.stack.last_mut()
}
fn lookup_private_prop<
'b,
Ret,
RetFn: Fn(&'b PrivateProp<'a>, &'b mut ClassBindings<'a>, bool) -> Ret,
>(
&'b mut self,
ident: &PrivateIdentifier<'a>,
ret_fn: RetFn,
) -> Ret {
for class in self.stack[1..].iter_mut().rev() {
if let Some(private_props) = &mut class.private_props
&& let Some(prop) = private_props.get(&ident.name)
{
return ret_fn(prop, &mut class.bindings, class.is_declaration);
}
}
unreachable!();
}
pub fn find_private_prop<'b>(
&'b mut self,
ident: &PrivateIdentifier<'a>,
) -> ResolvedPrivateProp<'a, 'b> {
self.lookup_private_prop(ident, move |prop, class_bindings, is_declaration| {
ResolvedPrivateProp {
prop_binding: &prop.binding,
class_bindings,
is_static: prop.is_static,
is_method: prop.is_method(),
is_accessor: prop.is_accessor(),
is_declaration,
}
})
}
pub fn find_readable_private_prop<'b>(
&'b mut self,
ident: &PrivateIdentifier<'a>,
) -> Option<ResolvedPrivateProp<'a, 'b>> {
self.lookup_private_prop(ident, move |prop, class_bindings, is_declaration| {
let prop_binding = if matches!(prop.method_kind, Some(MethodDefinitionKind::Set)) {
prop.binding2.as_ref()
} else {
Some(&prop.binding)
};
prop_binding.map(|prop_binding| ResolvedPrivateProp {
prop_binding,
class_bindings,
is_static: prop.is_static,
is_method: prop.is_method(),
is_accessor: prop.is_accessor(),
is_declaration,
})
})
}
pub fn find_writable_private_prop<'b>(
&'b mut self,
ident: &PrivateIdentifier<'a>,
) -> Option<ResolvedPrivateProp<'a, 'b>> {
self.lookup_private_prop(ident, move |prop, class_bindings, is_declaration| {
let prop_binding = if matches!(prop.method_kind, Some(MethodDefinitionKind::Set) | None)
{
Some(&prop.binding)
} else {
prop.binding2.as_ref()
};
prop_binding.map(|prop_binding| ResolvedPrivateProp {
prop_binding,
class_bindings,
is_static: prop.is_static,
is_method: prop.is_method(),
is_accessor: prop.is_accessor(),
is_declaration,
})
})
}
pub fn find_get_set_private_prop<'b>(
&'b mut self,
ident: &PrivateIdentifier<'a>,
) -> ResolvedGetSetPrivateProp<'a, 'b> {
self.lookup_private_prop(ident, move |prop, class_bindings, is_declaration| {
let (get_binding, set_binding) = match prop.method_kind {
Some(MethodDefinitionKind::Set) => (prop.binding2.as_ref(), Some(&prop.binding)),
Some(_) => (Some(&prop.binding), prop.binding2.as_ref()),
_ => (Some(&prop.binding), Some(&prop.binding)),
};
ResolvedGetSetPrivateProp {
get_binding,
set_binding,
class_bindings,
is_static: prop.is_static,
is_method: prop.is_method(),
is_accessor: prop.is_accessor(),
is_declaration,
}
})
}
}
pub(super) struct ResolvedPrivateProp<'a, 'b> {
pub prop_binding: &'b BoundIdentifier<'a>,
pub class_bindings: &'b mut ClassBindings<'a>,
pub is_static: bool,
pub is_method: bool,
pub is_accessor: bool,
pub is_declaration: bool,
}
pub(super) struct ResolvedGetSetPrivateProp<'a, 'b> {
pub get_binding: Option<&'b BoundIdentifier<'a>>,
pub set_binding: Option<&'b BoundIdentifier<'a>>,
pub class_bindings: &'b mut ClassBindings<'a>,
pub is_static: bool,
pub is_method: bool,
#[expect(unused)]
pub is_accessor: bool,
pub is_declaration: bool,
}
impl<'a> ClassProperties<'a> {
pub(super) fn current_class(&self) -> &ClassDetails<'a> {
self.classes_stack.last()
}
pub(super) fn current_class_mut(&mut self) -> &mut ClassDetails<'a> {
self.classes_stack.last_mut()
}
}