use std::convert::Infallible;
use rustpython_ast::{
Expr, Identifier, Stmt, StmtClassDef,
fold::{self, Fold},
};
const MANGLE_RED_ZONE: usize = 512 * 1024;
const MANGLE_GROW_SIZE: usize = 32 * 1024 * 1024;
fn is_private_name(ident: &str) -> bool {
ident.starts_with("__") && !ident.ends_with("__")
}
struct PrivateNameMangler {
class_stack: Vec<String>,
}
impl PrivateNameMangler {
fn mangle(&self, ident: &str) -> Option<String> {
let prefix = self.class_stack.last()?;
if prefix.is_empty() || !is_private_name(ident) {
return None;
}
Some(format!("_{prefix}{ident}"))
}
fn mangle_ident(&self, ident: &mut Identifier) {
if let Some(mangled) = self.mangle(ident.as_str()) {
*ident = Identifier::new(mangled);
}
}
fn fold_class_def<U>(&mut self, node: StmtClassDef<U>) -> Result<Stmt<U>, Infallible> {
let StmtClassDef { range, mut name, bases, keywords, body, decorator_list, type_params } =
node;
let body_prefix = name.trim_start_matches('_').to_owned();
self.mangle_ident(&mut name);
let bases = bases.into_iter().map(|b| self.fold_expr(b)).collect::<Result<Vec<_>, _>>()?;
let keywords =
keywords.into_iter().map(|k| self.fold_keyword(k)).collect::<Result<Vec<_>, _>>()?;
let decorator_list =
decorator_list.into_iter().map(|d| self.fold_expr(d)).collect::<Result<Vec<_>, _>>()?;
let type_params = type_params
.into_iter()
.map(|t| self.fold_type_param(t))
.collect::<Result<Vec<_>, _>>()?;
self.class_stack.push(body_prefix);
let body = body.into_iter().map(|s| self.fold_stmt(s)).collect::<Result<Vec<_>, _>>()?;
self.class_stack.pop();
Ok(Stmt::ClassDef(StmtClassDef {
range,
name,
bases,
keywords,
body,
decorator_list,
type_params,
}))
}
}
impl<U> Fold<U> for PrivateNameMangler {
type TargetU = U;
type Error = Infallible;
type UserContext = ();
fn will_map_user(&mut self, _user: &U) -> Self::UserContext {}
fn map_user(&mut self, user: U, (): ()) -> Result<Self::TargetU, Self::Error> {
Ok(user)
}
fn fold_expr(&mut self, node: Expr<U>) -> Result<Expr<U>, Infallible> {
stacker::maybe_grow(MANGLE_RED_ZONE, MANGLE_GROW_SIZE, move || {
let node = match node {
Expr::Attribute(mut attr) => {
self.mangle_ident(&mut attr.attr);
Expr::Attribute(attr)
}
Expr::Name(mut name) => {
self.mangle_ident(&mut name.id);
Expr::Name(name)
}
other => other,
};
fold::fold_expr(self, node)
})
}
fn fold_stmt(&mut self, node: Stmt<U>) -> Result<Stmt<U>, Infallible> {
stacker::maybe_grow(MANGLE_RED_ZONE, MANGLE_GROW_SIZE, move || self.fold_stmt_inner(node))
}
}
impl PrivateNameMangler {
fn fold_stmt_inner<U>(&mut self, node: Stmt<U>) -> Result<Stmt<U>, Infallible> {
match node {
Stmt::ClassDef(class_def) => self.fold_class_def(class_def),
Stmt::FunctionDef(mut func) => {
self.mangle_ident(&mut func.name);
fold::fold_stmt(self, Stmt::FunctionDef(func))
}
Stmt::AsyncFunctionDef(mut func) => {
self.mangle_ident(&mut func.name);
fold::fold_stmt(self, Stmt::AsyncFunctionDef(func))
}
Stmt::Global(mut global) => {
for name in &mut global.names {
self.mangle_ident(name);
}
Ok(Stmt::Global(global))
}
Stmt::Nonlocal(mut nonlocal) => {
for name in &mut nonlocal.names {
self.mangle_ident(name);
}
Ok(Stmt::Nonlocal(nonlocal))
}
other => fold::fold_stmt(self, other),
}
}
}
#[must_use]
pub fn mangle_private_names(suite: Vec<Stmt>) -> Vec<Stmt> {
let mut mangler = PrivateNameMangler { class_stack: Vec::new() };
suite
.into_iter()
.map(|stmt| match mangler.fold_stmt(stmt) {
Ok(folded) => folded,
Err(never) => match never {},
})
.collect()
}