Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions src/js_parser/lower/lower_decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,10 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
self.rewrite_private_accesses_in_stmts(core::slice::from_mut(&mut body), map);
data.body = body;
}
js_ast::StmtData::SFunction(data) => {
let stmts = data.func.body.stmts.slice_mut();
self.rewrite_private_accesses_in_stmts(stmts, map);
}
_ => {}
}
}
Expand Down Expand Up @@ -1299,6 +1303,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
BumpVec::<js_ast::StoreRef<G::ClassStaticBlock>>::new_in(bump);
let mut prefix_stmts = BumpVec::<Stmt>::new_in(bump);
let mut private_lowered_map: PrivateLoweredMap = PrivateLoweredMap::default();
let mut extracted_accessor_map: PrivateLoweredMap = PrivateLoweredMap::default();
let mut accessor_storage_counter: usize = 0;
let mut emitted_private_adds: HashMap<u32, ()> = HashMap::default();
let mut static_private_add_blocks = BumpVec::<Property>::new_in(bump);
Expand All @@ -1313,25 +1318,27 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
if cprop.kind == PropertyKind::ClassStaticBlock {
continue;
}
let is_private_key = cprop.key.is_some()
&& matches!(
cprop.key.unwrap().data,
js_ast::ExprData::EPrivateIdentifier(_)
);
if cprop.ts_decorators.len_u32() > 0 {
has_any_decorated = true;
if cprop.key.is_some()
&& matches!(
cprop.key.unwrap().data,
js_ast::ExprData::EPrivateIdentifier(_)
)
{
if is_private_key {
has_any_private = true;
lower_all_private = true;
break;
}
}
if cprop.key.is_some()
&& matches!(
cprop.key.unwrap().data,
js_ast::ExprData::EPrivateIdentifier(_)
)
{
if is_private_key {
has_any_private = true;
if let js_ast::ExprData::EPrivateIdentifier(pi) = &cprop.key.unwrap().data
&& p.static_init_private_refs
.contains_key(&pi.ref_.inner_index())
{
lower_all_private = true;
}
Comment thread
robobun marked this conversation as resolved.
}
}
if !lower_all_private && has_any_private && has_any_decorated {
Expand Down Expand Up @@ -1463,6 +1470,12 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
name
};
let wm_ref = p.new_sym(js_ast::symbol::Kind::Other, accessor_name);
if let Some(k) = prop.key
&& let js_ast::ExprData::EPrivateIdentifier(pi) = &k.data
{
extracted_accessor_map
.insert(pi.ref_.inner_index(), PrivateLoweredInfo::new(wm_ref));
}
let wme = p.new_weak_map_expr(loc);
prefix_stmts.push(p.var_decl(wm_ref, Some(wme), loc));

Expand Down Expand Up @@ -1884,6 +1897,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
if let Some(v) = &mut nprop.value {
p.rewrite_private_accesses_in_expr(v, &private_lowered_map);
}
if let Some(init) = &mut nprop.initializer {
p.rewrite_private_accesses_in_expr(init, &private_lowered_map);
}
if let Some(sb) = nprop.class_static_block_mut() {
p.rewrite_private_accesses_in_stmts(sb.stmts.slice_mut(), &private_lowered_map);
}
Expand All @@ -1893,6 +1909,22 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
p.rewrite_private_accesses_in_expr(ini, &private_lowered_map);
}
}
p.rewrite_private_accesses_in_stmts(
&mut constructor_inject_stmts,
&private_lowered_map,
);
for sp in static_private_add_blocks.iter_mut() {
if let Some(sb) = sp.class_static_block_mut() {
p.rewrite_private_accesses_in_stmts(sb.stmts.slice_mut(), &private_lowered_map);
}
}
}

for (k, v) in extracted_accessor_map.iter() {
private_lowered_map.insert(*k, *v);
}

if !private_lowered_map.is_empty() {
for entry in static_init_entries.iter_mut() {
if let Some(ini) = &mut entry.prop.initializer {
p.rewrite_private_accesses_in_expr(ini, &private_lowered_map);
Expand All @@ -1915,6 +1947,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
for elem in instance_field_decorate.iter_mut() {
p.rewrite_private_accesses_in_expr(elem, &private_lowered_map);
}
for elem in suffix_exprs.iter_mut() {
p.rewrite_private_accesses_in_expr(elem, &private_lowered_map);
}
p.rewrite_private_accesses_in_stmts(&mut pre_eval_stmts, &private_lowered_map);
p.rewrite_private_accesses_in_stmts(&mut prefix_stmts, &private_lowered_map);
}
Expand Down
5 changes: 5 additions & 0 deletions src/js_parser/p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,9 @@ pub struct P<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> {
/// Name from assignment context for anonymous decorated class expressions.
/// Set before visitExpr, consumed by lowerStandardDecoratorsImpl.
pub decorator_class_name: Option<&'a [u8]>,

pub class_static_init_depth: u32,
pub static_init_private_refs: HashMap<u32, ()>,
}

// Transposer helpers (Zig: `const ImportTransposer = ExpressionTransposer(P, ..., P.transposeImport);`)
Expand Down Expand Up @@ -9369,6 +9372,8 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
will_wrap_module_in_try_catch_for_using: false,
nearest_stmt_list: None,
decorator_class_name: None,
class_static_init_depth: 0,
static_init_private_refs: Default::default(),

jsx_transform,

Expand Down
29 changes: 29 additions & 0 deletions src/js_parser/visit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,10 @@
"only_scan_imports_and_do_not_visit must not run this."
);

let old_static_init_depth = self.class_static_init_depth;
let old_static_init_private_refs = core::mem::take(&mut self.static_init_private_refs);
self.class_static_init_depth = 0;

self.visit_ts_decorators(&mut class.ts_decorators);

if let Some(name) = class.class_name {
Expand Down Expand Up @@ -902,8 +906,10 @@
let csb_stmts = csb.stmts.slice();
let mut list = BumpVec::with_capacity_in(csb_stmts.len(), self.arena);
list.extend_from_slice(csb_stmts);
self.class_static_init_depth += 1;
self.visit_stmts(&mut list, StmtsKind::FnBody)
.expect("unreachable");
self.class_static_init_depth -= 1;
Comment thread
robobun marked this conversation as resolved.
csb.stmts = Vec::from_bump_vec(list);
self.pop_scope();

Expand Down Expand Up @@ -1012,6 +1018,11 @@

if let Some(val) = property.initializer {
// if (property.flags.is_static and )
let is_static_accessor_init = property.kind == PropertyKind::AutoAccessor
&& property.flags.contains(flags::Property::IsStatic);
if is_static_accessor_init {
self.class_static_init_depth += 1;
}
if let Some(name) = name_to_keep {
let was_anon = val.is_anonymous_named();
let prev_dcn2 = self.decorator_class_name;
Expand All @@ -1030,6 +1041,9 @@
} else {
self.visit_expr(property.initializer.as_mut().unwrap());
}
if is_static_accessor_init {
self.class_static_init_depth -= 1;
}
}

// manual restore for the three `defer`s above
Expand Down Expand Up @@ -1202,6 +1216,21 @@
// class name scope
self.pop_scope();

let class_static_init_private_refs = core::mem::take(&mut self.static_init_private_refs);
self.static_init_private_refs = old_static_init_private_refs;
self.class_static_init_depth = old_static_init_depth;
if !class_static_init_private_refs.is_empty() {
for property in class.properties.slice() {
if let Some(key) = property.key
&& let ExprData::EPrivateIdentifier(pi) = &key.data
&& class_static_init_private_refs.contains_key(&pi.ref_.inner_index())
{
self.static_init_private_refs
.insert(pi.ref_.inner_index(), ());
}
}
}

Check notice on line 1232 in src/js_parser/visit/mod.rs

View check run for this annotation

Claude / Claude Code Review

Per-class static_init_private_refs filter drops outer-private refs when nested class is inside outer's static block

🟣 Pre-existing, in scope: this filter only re-inserts refs that match *this* class's own private keys, so when an inner class nested inside an *outer class's static block* records an outer private (e.g. `#o`) from its own `static {}`, the ref is dropped here and never reaches Outer's pre-scan. Repro still unparseable: `class Outer { #o() {} accessor a = 1; static { class Inner { accessor b = 1; static { console.log(#o in new Outer()); } } } }` — Outer keeps `#o` native and its extracted static b
Comment thread
robobun marked this conversation as resolved.

shadow_ref.get()
}

Expand Down
4 changes: 4 additions & 0 deletions src/js_parser/visit/visit_binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,10 @@ impl BinaryExpressionVisitor {
let name = p.load_name_from_ref(private.ref_);
let result = p.find_symbol(e_.left.loc, name).expect("unreachable");
private.ref_ = result.r#ref;
if p.class_static_init_depth > 0 {
p.static_init_private_refs
.insert(result.r#ref.inner_index(), ());
}

// Unlike regular identifiers, there are no unbound private identifiers
let kind = p.symbols[result.r#ref.inner_index() as usize].kind;
Expand Down
4 changes: 4 additions & 0 deletions src/js_parser/visit/visit_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,10 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
let name = p.load_name_from_ref(private.ref_);
let result = p.find_symbol(e_.index.loc, name).expect("unreachable");
private.ref_ = result.r#ref;
if p.class_static_init_depth > 0 {
p.static_init_private_refs
.insert(result.r#ref.inner_index(), ());
}

// Unlike regular identifiers, there are no unbound private identifiers
let kind: js_ast::symbol::Kind =
Expand Down
Loading
Loading