Skip to content

Commit 9edfb1d

Browse files
committed
fix(napi/parser): fix unicode comment panic (#9084)
- closes #9079 I'm still leaving an existing TODO for `module_record` and `errors` span not being converted as they won't cause an immediate issue, but I'll work on that shortly.
1 parent 80f719e commit 9edfb1d

File tree

3 files changed

+32
-9
lines changed

3 files changed

+32
-9
lines changed

crates/oxc_ast/src/utf8_to_utf16.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl Utf8ToUtf16 {
2929
}
3030

3131
/// Convert all spans in the AST to UTF-16.
32-
pub fn convert(mut self, program: &mut Program<'_>) {
32+
pub fn convert(&mut self, program: &mut Program<'_>) {
3333
self.build_table(program.source_text);
3434
// Skip if source is entirely ASCII
3535
if self.translations.len() == 1 {
@@ -81,7 +81,8 @@ impl Utf8ToUtf16 {
8181
span.end = self.convert_offset(span.end);
8282
}
8383

84-
fn convert_offset(&self, utf8_offset: u32) -> u32 {
84+
/// Convert UTF-8 offset to UTF-16.
85+
pub fn convert_offset(&self, utf8_offset: u32) -> u32 {
8586
// Find the first entry in table *after* the UTF-8 offset.
8687
// The difference we need to subtract is recorded in the entry prior to it.
8788
let index =

napi/parser/src/lib.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,9 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption
6969
let allocator = Allocator::default();
7070
let source_type = get_source_type(filename, options);
7171
let mut ret = parse(&allocator, source_type, &source_text, options);
72-
if options.convert_span_utf16.unwrap_or(false) {
73-
// TODO: fix spans in `module_record` and `errors`
74-
Utf8ToUtf16::new().convert(&mut ret.program);
75-
}
76-
let program = serde_json::to_string(&ret.program).unwrap();
77-
7872
let errors = ret.errors.into_iter().map(OxcError::from).collect::<Vec<_>>();
7973

80-
let comments = ret
74+
let mut comments = ret
8175
.program
8276
.comments
8377
.iter()
@@ -93,6 +87,18 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption
9387
.collect::<Vec<Comment>>();
9488

9589
let module = EcmaScriptModule::from(&ret.module_record);
90+
91+
if options.convert_span_utf16.unwrap_or(false) {
92+
// TODO: fix spans in `module_record` and `errors`
93+
let mut converter = Utf8ToUtf16::new();
94+
converter.convert(&mut ret.program);
95+
for comment in &mut comments {
96+
comment.start = converter.convert_offset(comment.start);
97+
comment.end = converter.convert_offset(comment.end);
98+
}
99+
}
100+
let program = serde_json::to_string(&ret.program).unwrap();
101+
96102
ParseResult { source_text, program, module, comments, errors }
97103
}
98104

napi/parser/test/parse.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,22 @@ it('utf16 span', async () => {
105105
});
106106
expect(ret.program.end).toMatchInlineSnapshot(`4`);
107107
}
108+
{
109+
const code = `// ∞`;
110+
const ret = await parseAsync('test.js', code, {
111+
convertSpanUtf16: true,
112+
});
113+
expect(ret.comments).toMatchInlineSnapshot(`
114+
[
115+
{
116+
"end": 4,
117+
"start": 0,
118+
"type": "Line",
119+
"value": " ∞",
120+
},
121+
]
122+
`);
123+
}
108124
});
109125

110126
describe('error', () => {

0 commit comments

Comments
 (0)