Skip to content

Commit c6c6a5d

Browse files
ssrliveCopilot
andcommitted
fix: cluster 20 — eval/arguments as destructuring targets in strict mode
- Reject 'eval' and 'arguments' as binding names in array destructuring patterns - Reject 'eval' and 'arguments' as binding names in object destructuring patterns - Reject 'eval' and 'arguments' in rest patterns (...arguments) - Add check_destructuring_expr_strict() for for-of/for-in expression destructuring - Covers shorthand object destructuring ({arguments}) and nested patterns Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5eaf43d commit c6c6a5d

File tree

1 file changed

+72
-9
lines changed

1 file changed

+72
-9
lines changed

src/core/parser.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,7 @@ fn parse_for_statement(t: &[TokenData], index: &mut usize) -> Result<Statement,
951951
} else if let Some(expr) = init_expr {
952952
match expr {
953953
Expr::Property(_, _) | Expr::Index(_, _) | Expr::PrivateMember(_, _) | Expr::Array(_) | Expr::Object(_) => {
954+
check_destructuring_expr_strict(&expr)?;
954955
if is_for_await {
955956
StatementKind::ForAwaitOfExpr(expr, iterable, body_stmts)
956957
} else {
@@ -1119,6 +1120,7 @@ fn parse_for_statement(t: &[TokenData], index: &mut usize) -> Result<Statement,
11191120
| Expr::Var(_, _, _)
11201121
| Expr::Array(_)
11211122
| Expr::Object(_) => {
1123+
check_destructuring_expr_strict(&expr)?;
11221124
return Ok(Statement {
11231125
kind: Box::new(StatementKind::ForInExpr(expr, rhs, body_stmts)),
11241126
line,
@@ -3624,6 +3626,36 @@ fn parse_logical_and(tokens: &[TokenData], index: &mut usize) -> Result<Expr, JS
36243626
Ok(left)
36253627
}
36263628
}
3629+
/// Check if an expression used as a for-of/for-in destructuring target contains
3630+
/// `eval` or `arguments` as simple assignment targets (strict mode restriction).
3631+
fn check_destructuring_expr_strict(expr: &Expr) -> Result<(), JSError> {
3632+
if !strict_binding_checks() {
3633+
return Ok(());
3634+
}
3635+
match expr {
3636+
Expr::Var(name, _, _) if name == "eval" || name == "arguments" => Err(raise_parse_error!(&format!(
3637+
"'{}' can't be defined or assigned to in strict mode code",
3638+
name
3639+
))),
3640+
Expr::Array(elements) => {
3641+
for inner in elements.iter().flatten() {
3642+
match inner {
3643+
Expr::Spread(s) => check_destructuring_expr_strict(s)?,
3644+
Expr::Assign(lhs, _) => check_destructuring_expr_strict(lhs)?,
3645+
other => check_destructuring_expr_strict(other)?,
3646+
}
3647+
}
3648+
Ok(())
3649+
}
3650+
Expr::Object(pairs) => {
3651+
for (_, val, _, _) in pairs {
3652+
check_destructuring_expr_strict(val)?;
3653+
}
3654+
Ok(())
3655+
}
3656+
_ => Ok(()),
3657+
}
3658+
}
36273659
fn parse_logical_or(tokens: &[TokenData], index: &mut usize) -> Result<Expr, JSError> {
36283660
let left = parse_logical_and(tokens, index)?;
36293661
let mut look = *index;
@@ -3644,8 +3676,8 @@ fn parse_logical_or(tokens: &[TokenData], index: &mut usize) -> Result<Expr, JSE
36443676
/// Check if tokens in range [start..end) contain || or && at paren depth 0.
36453677
fn has_bare_logical_in_range(tokens: &[TokenData], start: usize, end: usize) -> bool {
36463678
let mut depth = 0usize;
3647-
for i in start..end {
3648-
match &tokens[i].token {
3679+
for td in tokens.iter().take(end).skip(start) {
3680+
match &td.token {
36493681
Token::LParen | Token::LBracket | Token::LBrace => depth += 1,
36503682
Token::RParen | Token::RBracket | Token::RBrace => {
36513683
depth = depth.saturating_sub(1);
@@ -4400,13 +4432,13 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
44004432
let msg = format!("Private field '{prop_name}' cannot be deleted");
44014433
return Err(raise_parse_error_with_token!(token_data, msg));
44024434
}
4403-
if let Expr::Var(..) = &inner {
4404-
if strict_binding_checks() {
4405-
return Err(raise_parse_error_with_token!(
4406-
token_data,
4407-
"Delete of an unqualified identifier in strict mode"
4408-
));
4409-
}
4435+
if let Expr::Var(..) = &inner
4436+
&& strict_binding_checks()
4437+
{
4438+
return Err(raise_parse_error_with_token!(
4439+
token_data,
4440+
"Delete of an unqualified identifier in strict mode"
4441+
));
44104442
}
44114443
Expr::Delete(Box::new(inner))
44124444
}
@@ -6425,6 +6457,12 @@ pub fn parse_array_destructuring_pattern(tokens: &[TokenData], index: &mut usize
64256457
if *index < tokens.len() && matches!(tokens[*index].token, Token::Spread) {
64266458
*index += 1;
64276459
if let Some(Token::Identifier(name)) = tokens.get(*index).map(|t| t.token.clone()) {
6460+
if strict_binding_checks() && (name == "eval" || name == "arguments") {
6461+
return Err(raise_parse_error_with_token!(
6462+
tokens[*index],
6463+
format!("'{}' can't be defined or assigned to in strict mode code", name)
6464+
));
6465+
}
64286466
*index += 1;
64296467
pattern.push(DestructuringElement::Rest(name));
64306468
} else if *index < tokens.len()
@@ -6503,6 +6541,12 @@ pub fn parse_array_destructuring_pattern(tokens: &[TokenData], index: &mut usize
65036541
}
65046542
pattern.push(DestructuringElement::NestedObject(nested_pattern, default_expr));
65056543
} else if let Some(Token::Identifier(name)) = tokens.get(*index).map(|t| t.token.clone()) {
6544+
if strict_binding_checks() && (name == "eval" || name == "arguments") {
6545+
return Err(raise_parse_error_with_token!(
6546+
tokens[*index],
6547+
format!("'{}' can't be defined or assigned to in strict mode code", name)
6548+
));
6549+
}
65066550
*index += 1;
65076551
let mut default_expr: Option<Box<Expr>> = None;
65086552
if *index < tokens.len() && matches!(tokens[*index].token, Token::Assign) {
@@ -6614,6 +6658,12 @@ pub fn parse_object_destructuring_pattern(tokens: &[TokenData], index: &mut usiz
66146658
if *index < tokens.len() && matches!(tokens[*index].token, Token::Spread) {
66156659
*index += 1;
66166660
if let Some(Token::Identifier(name)) = tokens.get(*index).map(|t| t.token.clone()) {
6661+
if strict_binding_checks() && (name == "eval" || name == "arguments") {
6662+
return Err(raise_parse_error_with_token!(
6663+
tokens[*index],
6664+
format!("'{}' can't be defined or assigned to in strict mode code", name)
6665+
));
6666+
}
66176667
*index += 1;
66186668
pattern.push(DestructuringElement::Rest(name));
66196669
} else {
@@ -6747,6 +6797,12 @@ pub fn parse_object_destructuring_pattern(tokens: &[TokenData], index: &mut usiz
67476797
}
67486798
DestructuringElement::NestedObject(nested, nested_default)
67496799
} else if let Some(Token::Identifier(name)) = tokens.get(*index).map(|t| t.token.clone()) {
6800+
if strict_binding_checks() && (name == "eval" || name == "arguments") {
6801+
return Err(raise_parse_error_with_token!(
6802+
tokens[*index],
6803+
format!("'{}' can't be defined or assigned to in strict mode code", name)
6804+
));
6805+
}
67506806
*index += 1;
67516807
let mut default_expr: Option<Box<Expr>> = None;
67526808
if *index < tokens.len() && matches!(tokens[*index].token, Token::Assign) {
@@ -6818,6 +6874,13 @@ pub fn parse_object_destructuring_pattern(tokens: &[TokenData], index: &mut usiz
68186874
if !is_identifier_key {
68196875
return Err(raise_parse_error_at!(tokens.get(*index)));
68206876
}
6877+
let key = key_name.clone().unwrap_or_default();
6878+
if strict_binding_checks() && (key == "eval" || key == "arguments") {
6879+
return Err(raise_parse_error!(&format!(
6880+
"'{}' can't be defined or assigned to in strict mode code",
6881+
key
6882+
)));
6883+
}
68216884
let mut init_tokens: Vec<TokenData> = Vec::new();
68226885
if *index < tokens.len() && matches!(tokens[*index].token, Token::Assign) {
68236886
*index += 1;

0 commit comments

Comments
 (0)