Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

swc_ecma_parser: Unexpected escape sequence in reserved word (2) #9227

Open
sebastiano-barrera opened this issue Jul 12, 2024 · 2 comments
Open

Comments

@sebastiano-barrera
Copy link

Describe the bug

Have swc_ecma_parser parse this test262 case: test/language/import/import-assertions/json-extensibility-array.js (others also exhibit this behavior).

(This is a re-post of #9214, this time with a proper reproduction link that works on the latest parser version, 0.147.0)

Input code

var obj = ({ bre\u0061k: 42 });

Config

(I'm only using swc_ecma_parser in my toy/learning JS impl; I'm not using the full swc and I don't have an .swcrc)

fwiw:
Syntax::Es(Default::default())
EsVersion::Es2015

Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.6.7&code=H4sIAAAAAAAAAytLLFLIT8pSsFXQqFZIKkqNKTUwMDPMtlIwMVKo1bTmAgDLr8KQIAAAAA%3D%3D&config=H4sIAAAAAAAAA1VPSQ7CMAy89xWRzxwACQ78gUdYwa1SZVMcJKKqfyfNUujNs3nsZRACZpbwEEseM%2FAYmMKOM8PJRvxkBkgaZBmUj3Dq6sybNKJmKtRaFYgYJoolxdfz5dYSoJ1j6onGGWXVmP47pTM%2BEPPRuFnRTpqOjUNrBeNe7yK2X2LyVC%2B4w8%2FUy%2FbFoPjZk3Xt%2BgVne8%2FLGAEAAA%3D%3D

SWC Info output

No response

Expected behavior

The above code is parsed as an object literal with property "var" (which is legal JS). This works in node.

Actual behavior

The following error is reported:

error: Unexpected escape sequence in reserved word: var
  --> <anon>:37:14
   |
37 | var obj = ({ v\u0061r: 42 });
   |              ^^^^^^^^

Version

0.147.0

Additional context

The relevant code that calls the parser is the following (just an extract; the original with full context is here):

use swc_ecma_ast::EsVersion;
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};

// ...
// content: String
let source_file = source_map.new_source_file(swc_common::FileName::Anon, content);
let input = StringInput::from(source_file.as_ref());

// ...

let lexer = Lexer::new(
    Syntax::Es(Default::default()),
    EsVersion::Es2015,
    input,
    None,
);
let mut parser = Parser::new_from(lexer);

// ...

let script_ast = parser
    .parse_script()
    .map_err(|e| {
        e.into_diagnostic(&err_handler).emit();
        error!("parse error")
    });
@kdy1 kdy1 self-assigned this Jul 13, 2024
@kdy1 kdy1 added this to the Planned milestone Jul 13, 2024
@kdy1 kdy1 assigned kdy1 and unassigned kdy1 Jul 13, 2024
@kdy1
Copy link
Member

kdy1 commented Jul 13, 2024

GFI guide

37 | var obj = ({ v\u0061r: 42 });

This is about an object lieral, and the relevant code lives at

let key = self.parse_prop_name()?;
if self.input.syntax().typescript()
&& !is_one_of!(self, '(', '[', ':', ',', '?', '=', '*', IdentName, Str, Num)
&& !(self.input.syntax().typescript() && is!(self, '<'))
&& !(is!(self, '}') && matches!(key, PropName::Ident(..)))
{
trace_cur!(self, parse_object_prop_error);
self.emit_err(self.input.cur_span(), SyntaxError::TS1005);
return Ok(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key,
value: Invalid {
span: span!(self, start),
}
.into(),
}))));
}
//
// {[computed()]: a,}
// { 'a': a, }
// { 0: 1, }
// { a: expr, }
if eat!(self, ':') {
let value = self.include_in_expr(true).parse_assignment_expr()?;
return Ok(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key,
value,
}))));
}

It seems like parse_prop_name has a bug.

parse_prop_name is declared as

pub(super) fn parse_prop_name(&mut self) -> PResult<PropName> {
trace_cur!(self, parse_prop_name);
let ctx = self.ctx();
self.with_ctx(Context {
in_property_name: true,
..ctx
})
.parse_with(|p| {
let start = cur_pos!(p);
let v = match *cur!(p, true) {
Token::Str { .. } => match bump!(p) {
Token::Str { value, raw } => PropName::Str(Str {
span: span!(p, start),
value,
raw: Some(raw),
}),
_ => unreachable!(),
},
Token::Num { .. } => match bump!(p) {
Token::Num { value, raw } => PropName::Num(Number {
span: span!(p, start),
value,
raw: Some(raw),
}),
_ => unreachable!(),
},
Token::BigInt { .. } => match bump!(p) {
Token::BigInt { value, raw } => PropName::BigInt(BigInt {
span: span!(p, start),
value,
raw: Some(raw),
}),
_ => unreachable!(),
},
Word(..) => match bump!(p) {
Word(w) => PropName::Ident(IdentName::new(w.into(), span!(p, start))),
_ => unreachable!(),
},
tok!('[') => {
bump!(p);
let inner_start = cur_pos!(p);
let mut expr = p.include_in_expr(true).parse_assignment_expr()?;
if p.syntax().typescript() && is!(p, ',') {
let mut exprs = vec![expr];
while eat!(p, ',') {
exprs.push(p.include_in_expr(true).parse_assignment_expr()?);
}
p.emit_err(span!(p, inner_start), SyntaxError::TS1171);
expr = Box::new(
SeqExpr {
span: span!(p, inner_start),
exprs,
}
.into(),
);
}
expect!(p, ']');
PropName::Computed(ComputedPropName {
span: span!(p, start),
expr,
})
}
_ => unexpected!(
p,
"identifier, string literal, numeric literal or [ for the computed key"
),
};
Ok(v)
})
}

But there's no relevant error reporting logic so my guess is that the error is from lexer. The relevant error reporting logic is at

self.error(
start,
SyntaxError::EscapeInReservedWord { word: word.into() },
)?

We need to patch this logic to allow escapes in reserved word in property names.

@kdy1 kdy1 removed their assignment Jul 13, 2024
@sebastiano-barrera
Copy link
Author

I'm going to take a stab at it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants