Skip to content

Commit 5eaf43d

Browse files
ssrliveCopilot
andcommitted
fix: clusters 11,15 — super/method/constructor context, arrow inherits super, exponentiation parenthesization, escaped of
- Add METHOD_CONTEXT and CONSTRUCTOR_CONTEXT thread-locals with stack save/restore - push_function_context() saves+clears method/constructor context (regular function resets) - push_arrow_function_context() preserves method/constructor context (arrows inherit super) - Add push_method_context() at class getter/setter, class methods, 6 object literal method locations - Add push_constructor_context() at class constructor body - super() requires in_constructor_context(); super.x/super[x] requires method or constructor - VM direct eval propagates method/constructor context for parser - Fix parenthesized unary before ** ((-1n)**-1n is valid) - Add escaped 'of' keyword checks in for-of parsing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent cfd9d94 commit 5eaf43d

2 files changed

Lines changed: 161 additions & 20 deletions

File tree

src/core/parser.rs

Lines changed: 143 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ thread_local! {
185185
static FORBID_IN: RefCell<usize> = const { RefCell::new(0) };
186186
static GENERATOR_CONTEXT: RefCell<usize> = const { RefCell::new(0) };
187187
static GENERATOR_CONTEXT_STACK: RefCell<Vec<usize>> = const { RefCell::new(Vec::new()) };
188+
/// >0 when parsing inside a method body (class/object method, getter, setter).
189+
/// super.x / super[x] are allowed when this is >0.
190+
static METHOD_CONTEXT: RefCell<usize> = const { RefCell::new(0) };
191+
static METHOD_CONTEXT_STACK: RefCell<Vec<usize>> = const { RefCell::new(Vec::new()) };
192+
/// >0 when parsing inside a constructor body (derived class).
193+
/// super() is allowed when this is >0.
194+
static CONSTRUCTOR_CONTEXT: RefCell<usize> = const { RefCell::new(0) };
195+
static CONSTRUCTOR_CONTEXT_STACK: RefCell<Vec<usize>> = const { RefCell::new(Vec::new()) };
188196
}
189197
fn forbid_in() -> bool {
190198
FORBID_IN.with(|c| *c.borrow() > 0)
@@ -214,23 +222,72 @@ fn push_generator_context() {
214222
fn pop_generator_context() {
215223
GENERATOR_CONTEXT.with(|c| *c.borrow_mut() -= 1);
216224
}
225+
fn in_method_context() -> bool {
226+
METHOD_CONTEXT.with(|c| *c.borrow() > 0)
227+
}
228+
fn in_constructor_context() -> bool {
229+
CONSTRUCTOR_CONTEXT.with(|c| *c.borrow() > 0)
230+
}
231+
fn push_method_context() {
232+
METHOD_CONTEXT.with(|c| *c.borrow_mut() += 1);
233+
}
234+
fn pop_method_context() {
235+
METHOD_CONTEXT.with(|c| *c.borrow_mut() -= 1);
236+
}
237+
fn push_constructor_context() {
238+
CONSTRUCTOR_CONTEXT.with(|c| *c.borrow_mut() += 1);
239+
}
240+
fn pop_constructor_context() {
241+
CONSTRUCTOR_CONTEXT.with(|c| *c.borrow_mut() -= 1);
242+
}
217243
fn in_function_context() -> bool {
218244
FUNCTION_CONTEXT.with(|c| *c.borrow() > 0)
219245
}
220-
/// Every function boundary saves+clears generator context automatically.
246+
/// Every function boundary saves+clears generator/method/constructor context automatically.
221247
fn push_function_context() {
222248
FUNCTION_CONTEXT.with(|c| *c.borrow_mut() += 1);
223-
let saved = GENERATOR_CONTEXT.with(|c| {
249+
let saved_gen = GENERATOR_CONTEXT.with(|c| {
250+
let prev = *c.borrow();
251+
*c.borrow_mut() = 0;
252+
prev
253+
});
254+
GENERATOR_CONTEXT_STACK.with(|s| s.borrow_mut().push(saved_gen));
255+
let saved_method = METHOD_CONTEXT.with(|c| {
256+
let prev = *c.borrow();
257+
*c.borrow_mut() = 0;
258+
prev
259+
});
260+
METHOD_CONTEXT_STACK.with(|s| s.borrow_mut().push(saved_method));
261+
let saved_ctor = CONSTRUCTOR_CONTEXT.with(|c| {
224262
let prev = *c.borrow();
225263
*c.borrow_mut() = 0;
226264
prev
227265
});
228-
GENERATOR_CONTEXT_STACK.with(|s| s.borrow_mut().push(saved));
266+
CONSTRUCTOR_CONTEXT_STACK.with(|s| s.borrow_mut().push(saved_ctor));
229267
}
230268
fn pop_function_context() {
231269
FUNCTION_CONTEXT.with(|c| *c.borrow_mut() -= 1);
232-
let saved = GENERATOR_CONTEXT_STACK.with(|s| s.borrow_mut().pop().unwrap_or(0));
233-
GENERATOR_CONTEXT.with(|c| *c.borrow_mut() = saved);
270+
let saved_gen = GENERATOR_CONTEXT_STACK.with(|s| s.borrow_mut().pop().unwrap_or(0));
271+
GENERATOR_CONTEXT.with(|c| *c.borrow_mut() = saved_gen);
272+
let saved_method = METHOD_CONTEXT_STACK.with(|s| s.borrow_mut().pop().unwrap_or(0));
273+
METHOD_CONTEXT.with(|c| *c.borrow_mut() = saved_method);
274+
let saved_ctor = CONSTRUCTOR_CONTEXT_STACK.with(|s| s.borrow_mut().pop().unwrap_or(0));
275+
CONSTRUCTOR_CONTEXT.with(|c| *c.borrow_mut() = saved_ctor);
276+
}
277+
/// Arrow functions inherit super/method/constructor context but NOT generator context
278+
fn push_arrow_function_context() {
279+
FUNCTION_CONTEXT.with(|c| *c.borrow_mut() += 1);
280+
let saved_gen = GENERATOR_CONTEXT.with(|c| {
281+
let prev = *c.borrow();
282+
*c.borrow_mut() = 0;
283+
prev
284+
});
285+
GENERATOR_CONTEXT_STACK.with(|s| s.borrow_mut().push(saved_gen));
286+
}
287+
fn pop_arrow_function_context() {
288+
FUNCTION_CONTEXT.with(|c| *c.borrow_mut() -= 1);
289+
let saved_gen = GENERATOR_CONTEXT_STACK.with(|s| s.borrow_mut().pop().unwrap_or(0));
290+
GENERATOR_CONTEXT.with(|c| *c.borrow_mut() = saved_gen);
234291
}
235292
pub(crate) fn in_await_context() -> bool {
236293
AWAIT_CONTEXT.with(|c| *c.borrow() > 0)
@@ -241,6 +298,18 @@ pub(crate) fn push_await_context() {
241298
pub(crate) fn pop_await_context() {
242299
AWAIT_CONTEXT.with(|c| *c.borrow_mut() -= 1);
243300
}
301+
pub(crate) fn push_method_context_for_eval() {
302+
METHOD_CONTEXT.with(|c| *c.borrow_mut() += 1);
303+
}
304+
pub(crate) fn pop_method_context_for_eval() {
305+
METHOD_CONTEXT.with(|c| *c.borrow_mut() -= 1);
306+
}
307+
pub(crate) fn push_constructor_context_for_eval() {
308+
CONSTRUCTOR_CONTEXT.with(|c| *c.borrow_mut() += 1);
309+
}
310+
pub(crate) fn pop_constructor_context_for_eval() {
311+
CONSTRUCTOR_CONTEXT.with(|c| *c.borrow_mut() -= 1);
312+
}
244313
fn strict_binding_checks() -> bool {
245314
STRICT_BINDING_CHECKS.with(|c| c.get())
246315
}
@@ -594,6 +663,12 @@ fn parse_for_statement(t: &[TokenData], index: &mut usize) -> Result<Statement,
594663
*index += 1;
595664
}
596665
if *index < t.len() && matches!(& t[* index].token, Token::Identifier(n) if n == "of") {
666+
if raw_identifier_source_has_escape(&t[*index]) {
667+
return Err(raise_parse_error_with_token!(
668+
t[*index],
669+
"'of' keyword must not contain Unicode escape sequences"
670+
));
671+
}
597672
*index += 1;
598673
let iterable = parse_assignment(t, index)?;
599674
while *index < t.len() && matches!(t[*index].token, Token::LineTerminator) {
@@ -774,6 +849,12 @@ fn parse_for_statement(t: &[TokenData], index: &mut usize) -> Result<Statement,
774849
*index += 1;
775850
}
776851
if *index < t.len() && matches!(t[* index].token, Token::Identifier(ref s) if s == "of") {
852+
if raw_identifier_source_has_escape(&t[*index]) {
853+
return Err(raise_parse_error_with_token!(
854+
t[*index],
855+
"'of' keyword must not contain Unicode escape sequences"
856+
));
857+
}
777858
*index += 1;
778859
let iterable = parse_assignment(t, index)?;
779860
if !matches!(t[*index].token, Token::RParen) {
@@ -3624,25 +3705,30 @@ fn parse_multiplicative(tokens: &[TokenData], index: &mut usize) -> Result<Expr,
36243705
})
36253706
}
36263707
fn parse_exponentiation(tokens: &[TokenData], index: &mut usize) -> Result<Expr, JSError> {
3708+
let start = *index;
36273709
let left = parse_primary(tokens, index, true)?;
36283710
if *index >= tokens.len() {
36293711
return Ok(left);
36303712
}
36313713
if matches!(tokens[*index].token, Token::Exponent) {
36323714
// Unary operators cannot be the base of exponentiation (spec: UpdateExpression ** ExponentiationExpression)
3633-
match &left {
3634-
Expr::Delete(_)
3635-
| Expr::Void(_)
3636-
| Expr::TypeOf(_)
3637-
| Expr::UnaryPlus(_)
3638-
| Expr::UnaryNeg(_)
3639-
| Expr::BitNot(_)
3640-
| Expr::LogicalNot(_) => {
3641-
return Err(crate::raise_syntax_error!(
3642-
"Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence"
3643-
));
3715+
// But parenthesized unary expressions are fine: (-1n) ** -1n is valid
3716+
let was_parenthesized = matches!(tokens[start].token, Token::LParen);
3717+
if !was_parenthesized {
3718+
match &left {
3719+
Expr::Delete(_)
3720+
| Expr::Void(_)
3721+
| Expr::TypeOf(_)
3722+
| Expr::UnaryPlus(_)
3723+
| Expr::UnaryNeg(_)
3724+
| Expr::BitNot(_)
3725+
| Expr::LogicalNot(_) => {
3726+
return Err(crate::raise_syntax_error!(
3727+
"Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence"
3728+
));
3729+
}
3730+
_ => {}
36443731
}
3645-
_ => {}
36463732
}
36473733
*index += 1;
36483734
let right = parse_exponentiation(tokens, index)?;
@@ -3983,7 +4069,9 @@ pub fn parse_class_body(t: &[TokenData], index: &mut usize) -> Result<Vec<ClassM
39834069
}
39844070
*index += 1;
39854071
push_function_context();
4072+
push_method_context();
39864073
let body = with_cleared_forbidden_await_identifier(|| parse_statement_block(t, index))?;
4074+
pop_method_context();
39874075
pop_function_context();
39884076
if is_getter {
39894077
if let Some(prop_expr) = prop_expr_opt {
@@ -4101,7 +4189,11 @@ pub fn parse_class_body(t: &[TokenData], index: &mut usize) -> Result<Vec<ClassM
41014189
}
41024190
*index += 1;
41034191
push_function_context();
4192+
push_method_context();
4193+
push_constructor_context();
41044194
let body = with_cleared_forbidden_await_identifier(|| parse_statement_block(t, index))?;
4195+
pop_constructor_context();
4196+
pop_method_context();
41054197
pop_function_context();
41064198
members.push(ClassMember::Constructor(params, body));
41074199
continue;
@@ -4128,6 +4220,7 @@ pub fn parse_class_body(t: &[TokenData], index: &mut usize) -> Result<Vec<ClassM
41284220
}
41294221
*index += 1;
41304222
push_function_context();
4223+
push_method_context();
41314224
let body = if is_generator {
41324225
push_generator_context();
41334226
let b = with_cleared_forbidden_await_identifier(|| parse_statement_block(t, index))?;
@@ -4136,6 +4229,7 @@ pub fn parse_class_body(t: &[TokenData], index: &mut usize) -> Result<Vec<ClassM
41364229
} else {
41374230
with_cleared_forbidden_await_identifier(|| parse_statement_block(t, index))?
41384231
};
4232+
pop_method_context();
41394233
pop_function_context();
41404234
if is_generator {
41414235
if let Some(expr) = computed_key_expr {
@@ -4666,6 +4760,12 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
46664760
Token::This => Expr::This,
46674761
Token::Super => {
46684762
if *index < tokens.len() && matches!(tokens[*index].token, Token::LParen) {
4763+
if !in_constructor_context() {
4764+
return Err(raise_parse_error_with_token!(
4765+
tokens[*index - 1],
4766+
"'super()' is only valid inside a class constructor"
4767+
));
4768+
}
46694769
*index += 1;
46704770
let mut args = Vec::new();
46714771
if *index < tokens.len() && !matches!(tokens[*index].token, Token::RParen) {
@@ -4690,6 +4790,12 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
46904790
*index += 1;
46914791
Expr::SuperCall(args)
46924792
} else if *index < tokens.len() && matches!(tokens[*index].token, Token::Dot) {
4793+
if !in_method_context() && !in_constructor_context() {
4794+
return Err(raise_parse_error_with_token!(
4795+
tokens[*index - 1],
4796+
"'super' property access is only valid inside a method"
4797+
));
4798+
}
46934799
*index += 1;
46944800
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::Identifier(_)) {
46954801
return Err(raise_parse_error_at!(tokens.get(*index)));
@@ -4745,6 +4851,12 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
47454851
}
47464852
} else if *index < tokens.len() && matches!(tokens[*index].token, Token::LBracket) {
47474853
// super[expr] — computed super property access
4854+
if !in_method_context() && !in_constructor_context() {
4855+
return Err(raise_parse_error_with_token!(
4856+
tokens[*index - 1],
4857+
"'super' property access is only valid inside a method"
4858+
));
4859+
}
47484860
*index += 1;
47494861
let key_expr = parse_assignment(tokens, index)?;
47504862
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBracket) {
@@ -4959,13 +5071,15 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
49595071
}
49605072
*index += 1;
49615073
push_function_context();
5074+
push_method_context();
49625075
if is_generator {
49635076
push_generator_context();
49645077
}
49655078
let body = parse_statements(tokens, index)?;
49665079
if is_generator {
49675080
pop_generator_context();
49685081
}
5082+
pop_method_context();
49695083
pop_function_context();
49705084
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBrace) {
49715085
return Err(raise_parse_error_at!(tokens.get(*index)));
@@ -5038,13 +5152,15 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
50385152
}
50395153
*index += 1;
50405154
push_function_context();
5155+
push_method_context();
50415156
if is_generator {
50425157
push_generator_context();
50435158
}
50445159
let body = parse_statements(tokens, index)?;
50455160
if is_generator {
50465161
pop_generator_context();
50475162
}
5163+
pop_method_context();
50485164
pop_function_context();
50495165
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBrace) {
50505166
return Err(raise_parse_error_at!(tokens.get(*index)));
@@ -5140,13 +5256,15 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
51405256
}
51415257
*index += 1;
51425258
push_function_context();
5259+
push_method_context();
51435260
if is_generator {
51445261
push_generator_context();
51455262
}
51465263
let body = parse_statements(tokens, index)?;
51475264
if is_generator {
51485265
pop_generator_context();
51495266
}
5267+
pop_method_context();
51505268
pop_function_context();
51515269
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBrace) {
51525270
return Err(raise_parse_error_at!(tokens.get(*index)));
@@ -5262,13 +5380,15 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
52625380
}
52635381
*index += 1;
52645382
push_function_context();
5383+
push_method_context();
52655384
if is_generator {
52665385
push_generator_context();
52675386
}
52685387
let body = parse_statements(tokens, index)?;
52695388
if is_generator {
52705389
pop_generator_context();
52715390
}
5391+
pop_method_context();
52725392
pop_function_context();
52735393
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBrace) {
52745394
return Err(raise_parse_error_at!(tokens.get(*index)));
@@ -5324,7 +5444,9 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
53245444
}
53255445
*index += 1;
53265446
push_function_context();
5447+
push_method_context();
53275448
let body = parse_statements(tokens, index)?;
5449+
pop_method_context();
53285450
pop_function_context();
53295451
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBrace) {
53305452
return Err(raise_parse_error_at!(tokens.get(*index)));
@@ -5355,7 +5477,9 @@ fn parse_primary(tokens: &[TokenData], index: &mut usize, allow_call: bool) -> R
53555477
}
53565478
*index += 1;
53575479
push_function_context();
5480+
push_method_context();
53585481
let body = parse_statements(tokens, index)?;
5482+
pop_method_context();
53595483
pop_function_context();
53605484
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBrace) {
53615485
return Err(raise_parse_error_at!(tokens.get(*index)));
@@ -6249,7 +6373,7 @@ fn parse_arrow_body_inner(tokens: &[TokenData], index: &mut usize, is_async: boo
62496373
}
62506374
if *index < tokens.len() && matches!(tokens[*index].token, Token::LBrace) {
62516375
*index += 1;
6252-
push_function_context();
6376+
push_arrow_function_context();
62536377
let body = if is_async {
62546378
push_await_context();
62556379
let r = parse_statements(tokens, index);
@@ -6258,7 +6382,7 @@ fn parse_arrow_body_inner(tokens: &[TokenData], index: &mut usize, is_async: boo
62586382
} else {
62596383
with_cleared_await_context(|| parse_statements(tokens, index))?
62606384
};
6261-
pop_function_context();
6385+
pop_arrow_function_context();
62626386
if *index >= tokens.len() || !matches!(tokens[*index].token, Token::RBrace) {
62636387
return Err(raise_parse_error_at!(tokens.get(*index)));
62646388
}

src/core/vm.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22044,7 +22044,24 @@ impl<'gc> VM<'gc> {
2204422044
crate::core::push_private_names_for_eval(all_names)
2204522045
});
2204622046
let statements = if is_direct {
22047-
do_parse(&code)?
22047+
// Set method/constructor context for super access in direct eval
22048+
let caller_ip = self.frames.last().map(|f| f.func_ip);
22049+
let in_method = caller_ip.map(|ip| self.chunk.method_function_ips.contains(&ip)).unwrap_or(false);
22050+
let in_ctor = caller_ip.map(|ip| self.chunk.class_constructor_ips.contains(&ip)).unwrap_or(false);
22051+
if in_method || in_ctor {
22052+
crate::core::parser::push_method_context_for_eval();
22053+
}
22054+
if in_ctor {
22055+
crate::core::parser::push_constructor_context_for_eval();
22056+
}
22057+
let result = do_parse(&code);
22058+
if in_ctor {
22059+
crate::core::parser::pop_constructor_context_for_eval();
22060+
}
22061+
if in_method || in_ctor {
22062+
crate::core::parser::pop_method_context_for_eval();
22063+
}
22064+
result?
2204822065
} else {
2204922066
// Indirect eval: relax strict-mode binding checks unless
2205022067
// the eval'd code itself starts with "use strict".

0 commit comments

Comments
 (0)