Skip to content

Commit

Permalink
fix(es/transforms): Fix capacity overflow with decorators (#8815)
Browse files Browse the repository at this point in the history
**Description:**

Fixes an incorrect capacity (found while investigating a segfault).

More details: denoland/deno#23187 (comment)
  • Loading branch information
dsherret authored Apr 4, 2024
1 parent c7f14ec commit 974f5c7
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 36 deletions.
79 changes: 50 additions & 29 deletions crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
collections::VecDeque,
iter::once,
mem::{take, transmute},
};
Expand All @@ -9,9 +10,9 @@ use swc_common::{util::take::Take, Spanned, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::{helper, helper_expr};
use swc_ecma_utils::{
alias_ident_for, constructor::inject_after_super, default_constructor, prepend_stmt,
private_ident, prop_name_to_expr_value, quote_ident, replace_ident, ExprFactory, IdentExt,
IdentRenamer,
alias_ident_for, constructor::inject_after_super, default_constructor,
is_maybe_branch_directive, prepend_stmt, private_ident, prop_name_to_expr_value, quote_ident,
replace_ident, ExprFactory, IdentExt, IdentRenamer,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};

Expand Down Expand Up @@ -1699,40 +1700,60 @@ impl VisitMut for Decorator202203 {
let old_extra_lets = self.extra_lets.take();
let old_extra_vars = self.extra_vars.take();

let mut new = Vec::with_capacity(n.len());
struct Insert {
index: usize,
item: Stmt,
}

for mut n in n.take() {
let mut inserts = VecDeque::new();

for (index, n) in n.iter_mut().enumerate() {
n.visit_mut_with(self);
if !self.extra_lets.is_empty() {
new.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
decls: self.extra_lets.take(),
declare: false,
}))))
}
if !self.pre_class_inits.is_empty() {
new.push(Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr: Expr::from_exprs(self.pre_class_inits.take()),
}))
inserts.push_back(Insert {
index,
item: Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr: Expr::from_exprs(self.pre_class_inits.take()),
}),
});
}
if !self.extra_lets.is_empty() {
inserts.push_back(Insert {
index,
item: Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
decls: self.extra_lets.take(),
declare: false,
}))),
});
}
new.push(n.take());
}

if !self.extra_vars.is_empty() {
prepend_stmt(
&mut new,
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: self.extra_vars.take(),
declare: false,
}
.into(),
);
let capacity = n.len() + inserts.len() + 1;
let mut new = Vec::with_capacity(capacity);
for (index, item) in n.take().into_iter().enumerate() {
if !self.extra_vars.is_empty() && !is_maybe_branch_directive(&item) {
new.push(
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: self.extra_vars.take(),
declare: false,
}
.into(),
);
}

while inserts.front().map(|v| v.index == index).unwrap_or(false) {
new.push(inserts.pop_front().unwrap().item);
}
new.push(item);
}
new.extend(inserts.into_iter().map(|v| v.item));

debug_assert!(new.len() <= capacity, "len: {} / {}", new.len(), capacity);
*n = new;

self.extra_vars = old_extra_vars;
Expand Down
19 changes: 12 additions & 7 deletions crates/swc_ecma_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2253,19 +2253,24 @@ pub fn opt_chain_test(
pub fn prepend_stmt<T: StmtLike>(stmts: &mut Vec<T>, stmt: T) {
let idx = stmts
.iter()
.position(|item| match item.as_stmt() {
Some(&Stmt::Expr(ExprStmt { ref expr, .. }))
if matches!(&**expr, Expr::Lit(Lit::Str(..))) =>
{
false
}
_ => true,
.position(|item| {
item.as_stmt()
.map(|s| !is_maybe_branch_directive(s))
.unwrap_or(true)
})
.unwrap_or(stmts.len());

stmts.insert(idx, stmt);
}

/// If the stmt is maybe a directive like `"use strict";`
pub fn is_maybe_branch_directive(stmt: &Stmt) -> bool {
match stmt {
Stmt::Expr(ExprStmt { ref expr, .. }) if matches!(&**expr, Expr::Lit(Lit::Str(..))) => true,
_ => false,
}
}

/// inject `stmts` after directives
pub fn prepend_stmts<T: StmtLike>(
to: &mut Vec<T>,
Expand Down

0 comments on commit 974f5c7

Please sign in to comment.