LibJS: Fix catch parameter and new.target regressions
- Restrict catch parameter conflict check to only direct children of the catch body block, not nested scopes - Set new_target_is_valid for dynamic function compilation (new Function) - Move check_parameters_post_body before flag restoration in parse_method_definition so generator methods inside static init blocks correctly allow 'await' as a parameter name
This commit is contained in:
parent
5374f0a85c
commit
f5eea4d232
5 changed files with 69 additions and 41 deletions
|
|
@ -904,6 +904,7 @@ pub unsafe extern "C" fn rust_compile_dynamic_function(
|
|||
};
|
||||
let mut parser = Parser::new(body_slice, ProgramType::Script);
|
||||
parser.flags.in_function_context = true;
|
||||
parser.flags.new_target_is_valid = true;
|
||||
match kind {
|
||||
ast::FunctionKind::Async | ast::FunctionKind::AsyncGenerator => {
|
||||
parser.flags.await_expression_is_valid = true;
|
||||
|
|
|
|||
|
|
@ -311,10 +311,6 @@ pub struct Parser<'a> {
|
|||
/// re-attempt inner positions during grouping expression re-parse.
|
||||
arrow_function_failed_positions: HashSet<usize>,
|
||||
|
||||
/// Catch parameter names used to detect redeclarations in catch body.
|
||||
/// Set while parsing a catch clause body, empty otherwise.
|
||||
catch_parameter_names: Vec<Utf16String>,
|
||||
|
||||
/// Regex literals whose compilation is deferred until after parsing.
|
||||
deferred_regexes: Vec<DeferredRegex>,
|
||||
}
|
||||
|
|
@ -364,7 +360,6 @@ impl<'a> Parser<'a> {
|
|||
scope_collector: ScopeCollector::new(),
|
||||
exported_names: HashSet::new(),
|
||||
function_table: FunctionTable::new(),
|
||||
catch_parameter_names: Vec::new(),
|
||||
arrow_function_failed_positions: HashSet::new(),
|
||||
deferred_regexes: Vec::new(),
|
||||
}
|
||||
|
|
@ -873,20 +868,6 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if a name conflicts with a catch clause parameter.
|
||||
/// https://tc39.es/ecma262/#sec-try-statement-static-semantics-early-errors
|
||||
pub(crate) fn check_catch_parameter_conflict(&mut self, name: &[u16]) {
|
||||
for catch_name in &self.catch_parameter_names {
|
||||
if catch_name.as_slice() == name {
|
||||
let name_str = String::from_utf16_lossy(name);
|
||||
self.syntax_error(&format!(
|
||||
"Identifier '{name_str}' already declared as catch parameter"
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for duplicate parameter names in arrow functions.
|
||||
/// Arrow functions always reject duplicates, regardless of strict mode.
|
||||
pub(crate) fn check_arrow_duplicate_parameters(&mut self, parameter_info: &[ParamInfo]) {
|
||||
|
|
|
|||
|
|
@ -164,7 +164,6 @@ impl Parser<'_> {
|
|||
);
|
||||
self.scope_collector
|
||||
.register_identifier(id.clone(), &value, Some(kind));
|
||||
self.check_catch_parameter_conflict(&value);
|
||||
}
|
||||
|
||||
VariableDeclaratorTarget::Identifier(id)
|
||||
|
|
@ -218,7 +217,6 @@ impl Parser<'_> {
|
|||
for (name, id) in &bound_names {
|
||||
self.scope_collector
|
||||
.register_identifier(id.clone(), name, None);
|
||||
self.check_catch_parameter_conflict(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -415,7 +413,6 @@ impl Parser<'_> {
|
|||
declaration_line,
|
||||
declaration_column,
|
||||
);
|
||||
self.check_catch_parameter_conflict(&fn_name);
|
||||
|
||||
let fn_name_for_scope = if fn_name.is_empty() {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -2597,14 +2597,17 @@ impl Parser<'_> {
|
|||
self.scope_collector.close_scope();
|
||||
self.pattern_bound_names = saved_pattern_bound_names;
|
||||
|
||||
self.flags.in_class_static_init_block = saved_static_init;
|
||||
self.flags.in_class_field_initializer = saved_field_init;
|
||||
self.flags.new_target_is_valid = saved_new_target;
|
||||
|
||||
// Check parameters before restoring flags so that the method's
|
||||
// context is used (e.g. in_class_static_init_block must remain
|
||||
// false to allow `await` as a parameter name in generators).
|
||||
if has_use_strict || fn_kind != FunctionKind::Normal {
|
||||
self.check_parameters_post_body(&parsed.parameter_info, has_use_strict, fn_kind);
|
||||
}
|
||||
|
||||
self.flags.in_class_static_init_block = saved_static_init;
|
||||
self.flags.in_class_field_initializer = saved_field_init;
|
||||
self.flags.new_target_is_valid = saved_new_target;
|
||||
|
||||
insights.might_need_arguments_object = self.flags.function_might_need_arguments_object;
|
||||
self.flags.function_might_need_arguments_object = saved_might_need_arguments;
|
||||
|
||||
|
|
|
|||
|
|
@ -813,24 +813,70 @@ impl Parser<'_> {
|
|||
None
|
||||
};
|
||||
|
||||
// Store catch parameter names so that lexical and function
|
||||
// declarations in the body can be checked for redeclaration.
|
||||
let saved_catch_names = std::mem::take(&mut self.catch_parameter_names);
|
||||
match ¶meter {
|
||||
Some(CatchBinding::Identifier(id)) => {
|
||||
self.catch_parameter_names.push(id.name.clone());
|
||||
}
|
||||
Some(CatchBinding::BindingPattern(_)) => {
|
||||
for (name, _) in &self.pattern_bound_names {
|
||||
self.catch_parameter_names.push(name.clone());
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
// Collect catch parameter names for post-body validation.
|
||||
let catch_names: Vec<Utf16String> = match ¶meter {
|
||||
Some(CatchBinding::Identifier(id)) => vec![id.name.clone()],
|
||||
Some(CatchBinding::BindingPattern(_)) => self
|
||||
.pattern_bound_names
|
||||
.iter()
|
||||
.map(|(n, _)| n.clone())
|
||||
.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
|
||||
let body = self.parse_block_statement();
|
||||
|
||||
self.catch_parameter_names = saved_catch_names;
|
||||
// https://tc39.es/ecma262/#sec-try-statement-static-semantics-early-errors
|
||||
// It is a Syntax Error if any element of the BoundNames of
|
||||
// CatchParameter also occurs in the LexicallyDeclaredNames of Block.
|
||||
if !catch_names.is_empty()
|
||||
&& let StatementKind::Block(ref scope) = body.inner
|
||||
{
|
||||
for child in &scope.borrow().children {
|
||||
match &child.inner {
|
||||
StatementKind::VariableDeclaration { kind, declarations }
|
||||
if *kind != DeclarationKind::Var =>
|
||||
{
|
||||
for decl in declarations {
|
||||
if let VariableDeclaratorTarget::Identifier(ref id) = decl.target {
|
||||
for cn in &catch_names {
|
||||
if cn.as_slice() == id.name.as_slice() {
|
||||
let n = String::from_utf16_lossy(cn);
|
||||
self.syntax_error(&format!(
|
||||
"Identifier '{n}' already declared as catch parameter"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StatementKind::FunctionDeclaration { name: Some(id), .. } => {
|
||||
for cn in &catch_names {
|
||||
if cn.as_slice() == id.name.as_slice() {
|
||||
let n = String::from_utf16_lossy(cn);
|
||||
self.syntax_error(&format!(
|
||||
"Identifier '{n}' already declared as catch parameter"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
StatementKind::ClassDeclaration(data) => {
|
||||
if let Some(ref id) = data.name {
|
||||
for cn in &catch_names {
|
||||
if cn.as_slice() == id.name.as_slice() {
|
||||
let n = String::from_utf16_lossy(cn);
|
||||
self.syntax_error(&format!(
|
||||
"Identifier '{n}' already declared as catch parameter"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.scope_collector.close_scope();
|
||||
|
||||
CatchClause {
|
||||
|
|
|
|||
Loading…
Reference in a new issue