Skip to content
Open
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
142 changes: 138 additions & 4 deletions src/js_parser/lower/lower_decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,10 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
}
js_ast::ExprData::EObject(e) => {
for prop in e.properties.slice_mut() {
// Computed keys; non-computed keys are literals (no-op).
if let Some(k) = &mut prop.key {
self.rewrite_expr(k, kind);
}
if let Some(v) = &mut prop.value {
self.rewrite_expr(v, kind);
}
Expand All @@ -489,24 +493,98 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
self.rewrite_expr(&mut part.value, kind);
}
}
js_ast::ExprData::EAwait(e) => self.rewrite_expr(&mut e.value, kind),
js_ast::ExprData::EYield(e) => {
if let Some(v) = &mut e.value {
self.rewrite_expr(v, kind);
}
}
js_ast::ExprData::EImport(e) => {
self.rewrite_expr(&mut e.expr, kind);
self.rewrite_expr(&mut e.options, kind);
}
js_ast::ExprData::EArrow(e) => {
// Arrows inherit `this`: defaults and body get both kinds.
self.rewrite_args(e.args.slice_mut(), kind);
let stmts = e.body.stmts.slice_mut();
self.rewrite_stmts(stmts, kind);
}
js_ast::ExprData::EFunction(e) => match kind {
RewriteKind::ReplaceThis { .. } => {}
RewriteKind::ReplaceRef { .. } => {
self.rewrite_args(e.func.args.slice_mut(), kind);
let stmts = e.func.body.stmts.slice_mut();
if !stmts.is_empty() {
self.rewrite_stmts(stmts, kind);
}
}
},
js_ast::ExprData::EClass(_) => {}
js_ast::ExprData::EClass(e) => self.rewrite_class(e, kind),
_ => {}
}
}

/// Rewrite inside a nested class. The extends clause and computed keys
/// evaluate in the enclosing scope, so they get both kinds. Method
/// bodies, field initializers, and static blocks bind their own `this`,
/// so they are only walked for `ReplaceRef` (ref identity keeps
/// shadowing bindings intact).
fn rewrite_class(&mut self, class: &mut G::Class, kind: RewriteKind) {
if let Some(ext) = &mut class.extends {
self.rewrite_expr(ext, kind);
}
for prop in class.properties.slice_mut() {
if let Some(k) = &mut prop.key {
self.rewrite_expr(k, kind);
}
if matches!(kind, RewriteKind::ReplaceThis { .. }) {
continue;
}
if let Some(v) = &mut prop.value {
self.rewrite_expr(v, kind);
}
if let Some(ini) = &mut prop.initializer {
self.rewrite_expr(ini, kind);
}
if let Some(mut sb) = prop.class_static_block {
self.rewrite_stmts(sb.stmts.slice_mut(), kind);
}
}
}

fn rewrite_args(&mut self, args: &mut [G::Arg], kind: RewriteKind) {
for arg in args.iter_mut() {
self.rewrite_binding(&mut arg.binding, kind);
if let Some(d) = &mut arg.default {
self.rewrite_expr(d, kind);
}
}
}

/// Rewrite default values (and computed keys) in destructuring patterns.
fn rewrite_binding(&mut self, binding: &mut js_ast::Binding, kind: RewriteKind) {
match &mut binding.data {
js_ast::b::B::BIdentifier(_) | js_ast::b::B::BMissing(_) => {}
js_ast::b::B::BArray(arr) => {
for item in arr.items_mut() {
self.rewrite_binding(&mut item.binding, kind);
if let Some(d) = &mut item.default_value {
self.rewrite_expr(d, kind);
}
}
}
js_ast::b::B::BObject(obj) => {
for prop in obj.properties_mut() {
self.rewrite_expr(&mut prop.key, kind);
self.rewrite_binding(&mut prop.value, kind);
if let Some(d) = &mut prop.default_value {
self.rewrite_expr(d, kind);
}
}
}
}
}

fn rewrite_stmts(&mut self, stmts: &mut [Stmt], kind: RewriteKind) {
for cur_stmt in stmts.iter_mut() {
let cur_loc = cur_stmt.loc;
Expand All @@ -524,6 +602,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
}
js_ast::StmtData::SLocal(local) => {
for decl in local.decls.slice_mut() {
self.rewrite_binding(&mut decl.binding, kind);
if let Some(v) = &mut decl.value {
self.rewrite_expr(v, kind);
}
Expand Down Expand Up @@ -565,6 +644,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
data.body = body;
}
js_ast::StmtData::SForIn(data) => {
let mut init = data.init;
self.rewrite_stmts(core::slice::from_mut(&mut init), kind);
data.init = init;
let mut v = data.value;
self.rewrite_expr(&mut v, kind);
data.value = v;
Expand All @@ -573,6 +655,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
data.body = body;
}
js_ast::StmtData::SForOf(data) => {
let mut init = data.init;
self.rewrite_stmts(core::slice::from_mut(&mut init), kind);
data.init = init;
let mut v = data.value;
self.rewrite_expr(&mut v, kind);
data.value = v;
Expand Down Expand Up @@ -613,6 +698,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
let body = data.body.slice_mut();
self.rewrite_stmts(body, kind);
if let Some(c) = &mut data.catch_ {
if let Some(b) = &mut c.binding {
self.rewrite_binding(b, kind);
}
let cb = c.body.slice_mut();
self.rewrite_stmts(cb, kind);
}
Expand All @@ -634,6 +722,16 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
self.rewrite_stmts(core::slice::from_mut(&mut body), kind);
data.body = body;
}
js_ast::StmtData::SFunction(data) => match kind {
// Function declarations bind their own `this`.
RewriteKind::ReplaceThis { .. } => {}
RewriteKind::ReplaceRef { .. } => {
self.rewrite_args(data.func.args.slice_mut(), kind);
let stmts = data.func.body.stmts.slice_mut();
self.rewrite_stmts(stmts, kind);
}
},
js_ast::StmtData::SClass(data) => self.rewrite_class(&mut data.class, kind),
_ => {}
}
}
Expand Down Expand Up @@ -1226,8 +1324,13 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
}
}

// For named class expressions: swap to expr_class_ref for suffix ops
// For named class expressions: swap to expr_class_ref for suffix ops.
// The inner class name binding only exists inside the class body, so
// static initializers relocated into the suffix chain must reference
// the hoisted `_class` temp instead (class declarations keep their
// module-level binding, so no replacement is needed there).
let mut original_class_name_for_decorator: Option<&'a [u8]> = None;
let mut relocated_name_rewrite: Option<RewriteKind> = None;
if is_expr
&& !expr_class_is_anonymous
&& let Some(ecr) = expr_class_ref
Expand All @@ -1238,6 +1341,10 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
.original_name
.slice(),
);
relocated_name_rewrite = Some(RewriteKind::ReplaceRef {
old: class_name_ref,
new: ecr,
});
class_name_ref = ecr;
class_name_loc = loc;
}
Expand Down Expand Up @@ -1534,7 +1641,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
..Default::default()
});

let init_val = prop
let mut init_val = prop
.initializer
.unwrap_or_else(|| p.new_expr(E::Undefined {}, loc));
if !prop.flags.contains(Flags::Property::IsStatic) {
Expand All @@ -1549,6 +1656,18 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
loc,
));
} else {
// Relocated out of the class body: `this` and the
// inner class name must become the class reference.
p.rewrite_expr(
&mut init_val,
RewriteKind::ReplaceThis {
ref_: class_name_ref,
loc: class_name_loc,
},
);
if let Some(rk) = relocated_name_rewrite {
p.rewrite_expr(&mut init_val, rk);
}
let cn_e = p.use_ref(class_name_ref, class_name_loc);
let wm_e3 = p.use_ref(wm_ref, loc);
suffix_exprs.push(p.call_rt(
Expand Down Expand Up @@ -2021,6 +2140,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
loc: class_name_loc,
},
);
if let Some(rk) = relocated_name_rewrite {
p.rewrite_stmts(stmts_slice, rk);
}

let all_exprs = stmts_slice
.iter()
Expand Down Expand Up @@ -2078,7 +2200,19 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
loc,
));
run_args.push(p.use_ref(class_name_ref, class_name_loc));
if let Some(init_val) = entry.prop.initializer {
if let Some(mut init_val) = entry.prop.initializer {
// Relocated out of the class body: `this` and the
// inner class name must become the class reference.
p.rewrite_expr(
&mut init_val,
RewriteKind::ReplaceThis {
ref_: class_name_ref,
loc: class_name_loc,
},
);
if let Some(rk) = relocated_name_rewrite {
p.rewrite_expr(&mut init_val, rk);
}
Comment thread
claude[bot] marked this conversation as resolved.
run_args.push(init_val);
}
let run_args_list = ExprNodeList::from_bump_vec(run_args);
Expand Down
Loading
Loading