1use std::{
2 io,
3 io::{BufReader, Read},
4};
5
6use bitvec::{field::BitField, order::Lsb0, vec::BitVec};
7use serde_json::Value;
8
9use crate::{
10 errors::{Error, Result},
11 hermes::decode_hermes,
12 jsontypes::RawSourceMap,
13 types::{DecodedMap, RawToken, SourceMap, SourceMapIndex, SourceMapSection},
14 vlq::parse_vlq_segment_into,
15};
16
17const DATA_PREAMBLE: &str = "data:application/json;base64,";
18
19#[derive(PartialEq, Eq)]
20enum HeaderState {
21 Undecided,
22 Junk,
23 AwaitingNewline,
24 PastHeader,
25}
26
27pub struct StripHeaderReader<R: Read> {
28 r: R,
29 header_state: HeaderState,
30}
31
32impl<R: Read> StripHeaderReader<R> {
33 pub fn new(reader: R) -> StripHeaderReader<R> {
34 StripHeaderReader {
35 r: reader,
36 header_state: HeaderState::Undecided,
37 }
38 }
39}
40
41fn is_junk_json(byte: u8) -> bool {
42 byte == b')' || byte == b']' || byte == b'}' || byte == b'\''
43}
44
45impl<R: Read> Read for StripHeaderReader<R> {
46 #[inline(always)]
47 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
48 if self.header_state == HeaderState::PastHeader {
49 return self.r.read(buf);
50 }
51 self.strip_head_read(buf)
52 }
53}
54
55impl<R: Read> StripHeaderReader<R> {
56 fn strip_head_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
57 let mut backing = vec![0; buf.len()];
58 let local_buf: &mut [u8] = &mut backing;
59
60 loop {
61 let read = self.r.read(local_buf)?;
62 if read == 0 {
63 return Ok(0);
64 }
65 for (offset, &byte) in local_buf[0..read].iter().enumerate() {
66 self.header_state = match self.header_state {
67 HeaderState::Undecided => {
68 if is_junk_json(byte) {
69 HeaderState::Junk
70 } else {
71 buf[..read].copy_from_slice(&local_buf[..read]);
72 self.header_state = HeaderState::PastHeader;
73 return Ok(read);
74 }
75 }
76 HeaderState::Junk => {
77 if byte == b'\r' {
78 HeaderState::AwaitingNewline
79 } else if byte == b'\n' {
80 HeaderState::PastHeader
81 } else {
82 HeaderState::Junk
83 }
84 }
85 HeaderState::AwaitingNewline => {
86 if byte == b'\n' {
87 HeaderState::PastHeader
88 } else {
89 Err(io::Error::new(
90 io::ErrorKind::InvalidData,
91 "expected newline",
92 ))?
93 }
94 }
95 HeaderState::PastHeader => {
96 let rem = read - offset;
97 buf[..rem].copy_from_slice(&local_buf[offset..read]);
98 return Ok(rem);
99 }
100 };
101 }
102 }
103 }
104}
105
106pub fn strip_junk_header(slice: &[u8]) -> io::Result<&[u8]> {
107 if slice.is_empty() || !is_junk_json(slice[0]) {
108 return Ok(slice);
109 }
110 let mut need_newline = false;
111 for (idx, &byte) in slice.iter().enumerate() {
112 if need_newline && byte != b'\n' {
113 Err(io::Error::new(
114 io::ErrorKind::InvalidData,
115 "expected newline",
116 ))?
117 } else if is_junk_json(byte) {
118 continue;
119 } else if byte == b'\r' {
120 need_newline = true;
121 } else if byte == b'\n' {
122 return Ok(&slice[idx..]);
123 }
124 }
125 Ok(&slice[slice.len()..])
126}
127
128pub(crate) fn decode_rmi(rmi_str: &str, val: &mut BitVec<u8, Lsb0>) -> Result<()> {
130 val.clear();
131 val.resize(rmi_str.len() * 6, false);
132
133 for (idx, &byte) in rmi_str.as_bytes().iter().enumerate() {
134 let byte = match byte {
135 b'A'..=b'Z' => byte - b'A',
136 b'a'..=b'z' => byte - b'a' + 26,
137 b'0'..=b'9' => byte - b'0' + 52,
138 b'+' => 62,
139 b'/' => 63,
140 _ => {
141 return Err(Error::InvalidBase64(byte as char));
142 }
143 };
144
145 val[6 * idx..6 * (idx + 1)].store_le::<u8>(byte);
146 }
147
148 Ok(())
149}
150
151pub fn decode_regular(rsm: RawSourceMap) -> Result<SourceMap> {
152 let mut dst_col;
153
154 let mut running_src_id = 0;
157 let mut running_src_line = 0;
158 let mut running_src_col = 0;
159 let mut running_name_id = 0;
160
161 let names = rsm.names.unwrap_or_default();
162 let sources = rsm.sources.unwrap_or_default();
163 let range_mappings = rsm.range_mappings.unwrap_or_default();
164 let mappings = rsm.mappings.unwrap_or_default();
165 let allocation_size = mappings.matches(&[',', ';'][..]).count() + 10;
166 let mut tokens = Vec::with_capacity(allocation_size);
167
168 let mut nums = Vec::with_capacity(6);
169 let mut rmi = BitVec::new();
170
171 for (dst_line, (line, rmi_str)) in mappings
172 .split(';')
173 .zip(range_mappings.split(';').chain(std::iter::repeat("")))
174 .enumerate()
175 {
176 if line.is_empty() {
177 continue;
178 }
179
180 dst_col = 0;
181
182 decode_rmi(rmi_str, &mut rmi)?;
183
184 for (line_index, segment) in line.split(',').enumerate() {
185 if segment.is_empty() {
186 continue;
187 }
188
189 nums.clear();
190 parse_vlq_segment_into(segment, &mut nums)?;
191 match nums.len() {
192 1 | 4 | 5 => {}
193 _ => return Err(Error::BadSegmentSize(nums.len() as u32)),
194 }
195
196 dst_col = (i64::from(dst_col) + nums[0]) as u32;
197
198 let mut current_src_id = !0;
202 let mut current_src_line = !0;
203 let mut current_src_col = !0;
204 let mut current_name_id = !0;
205
206 if nums.len() > 1 {
207 running_src_id = (i64::from(running_src_id) + nums[1]) as u32;
208
209 if running_src_id >= sources.len() as u32 {
210 return Err(Error::BadSourceReference(running_src_id));
211 }
212
213 running_src_line = (i64::from(running_src_line) + nums[2]) as u32;
214 running_src_col = (i64::from(running_src_col) + nums[3]) as u32;
215
216 current_src_id = running_src_id;
217 current_src_line = running_src_line;
218 current_src_col = running_src_col;
219
220 if nums.len() > 4 {
221 running_name_id = (i64::from(running_name_id) + nums[4]) as u32;
222 if running_name_id >= names.len() as u32 {
223 return Err(Error::BadNameReference(running_name_id));
224 }
225 current_name_id = running_name_id;
226 }
227 }
228
229 let is_range = rmi.get(line_index).map(|v| *v).unwrap_or_default();
230
231 tokens.push(RawToken {
232 dst_line: dst_line as u32,
233 dst_col,
234 src_line: current_src_line,
235 src_col: current_src_col,
236 src_id: current_src_id,
237 name_id: current_name_id,
238 is_range,
239 });
240 }
241 }
242
243 let sources = sources
244 .into_iter()
245 .map(Option::unwrap_or_default)
246 .map(Into::into)
247 .collect();
248
249 let names = names
252 .into_iter()
253 .map(|val| match val {
254 Value::String(s) => s.into(),
255 Value::Number(num) => num.to_string().into(),
256 _ => "".into(),
257 })
258 .collect::<Vec<_>>();
259
260 let file = rsm.file.map(|val| match val {
262 Value::String(s) => s.into(),
263 _ => "<invalid>".into(),
264 });
265
266 let source_content = rsm
267 .sources_content
268 .map(|x| x.into_iter().map(|v| v.map(Into::into)).collect::<Vec<_>>());
269
270 let mut sm = SourceMap::new(file, tokens, names, sources, source_content);
271 sm.set_scopes(rsm.scopes);
272 sm.set_source_root(rsm.source_root);
273 sm.set_debug_id(rsm.debug_id.or(rsm._debug_id_new));
276 if let Some(ignore_list) = rsm.ignore_list {
277 for idx in ignore_list {
278 sm.add_to_ignore_list(idx);
279 }
280 }
281
282 Ok(sm)
283}
284
285fn decode_index(rsm: RawSourceMap) -> Result<SourceMapIndex> {
286 let mut sections = vec![];
287
288 for mut raw_section in rsm.sections.unwrap_or_default() {
289 sections.push(SourceMapSection::new(
290 (raw_section.offset.line, raw_section.offset.column),
291 raw_section.url,
292 match raw_section.map.take() {
293 Some(map) => Some(decode_common(*map)?),
294 None => None,
295 },
296 ));
297 }
298
299 sections.sort_by_key(SourceMapSection::get_offset);
300
301 let file = rsm.file.map(|val| match val {
303 Value::String(s) => s.into(),
304 _ => "<invalid>".into(),
305 });
306
307 Ok(SourceMapIndex::new_ram_bundle_compatible(
308 file,
309 sections,
310 rsm.x_facebook_offsets,
311 rsm.x_metro_module_paths,
312 )
313 .with_debug_id(rsm._debug_id_new.or(rsm.debug_id)))
314}
315
316fn decode_common(rsm: RawSourceMap) -> Result<DecodedMap> {
317 Ok(if rsm.sections.is_some() {
318 DecodedMap::Index(decode_index(rsm)?)
319 } else if rsm.x_facebook_sources.is_some() {
320 DecodedMap::Hermes(decode_hermes(rsm)?)
321 } else {
322 DecodedMap::Regular(decode_regular(rsm)?)
323 })
324}
325
326pub fn decode<R: Read>(rdr: R) -> Result<DecodedMap> {
331 let mut rdr = StripHeaderReader::new(rdr);
332 let mut rdr = BufReader::new(&mut rdr);
333 let rsm: RawSourceMap = serde_json::from_reader(&mut rdr)?;
334 decode_common(rsm)
335}
336
337pub fn decode_slice(slice: &[u8]) -> Result<DecodedMap> {
342 let content = strip_junk_header(slice)?;
343 let rsm: RawSourceMap = serde_json::from_slice(content)?;
344 decode_common(rsm)
345}
346
347pub fn decode_data_url(url: &str) -> Result<DecodedMap> {
349 if !url.starts_with(DATA_PREAMBLE) {
350 return Err(Error::InvalidDataUrl);
351 }
352 let data_b64 = &url[DATA_PREAMBLE.len()..];
353 let data = data_encoding::BASE64
354 .decode(data_b64.as_bytes())
355 .map_err(|_| Error::InvalidDataUrl)?;
356 decode_slice(&data[..])
357}
358
359#[cfg(test)]
360mod tests {
361 use std::io::{self, BufRead};
362
363 use super::*;
364
365 #[test]
366 fn test_strip_header() {
367 let input: &[_] = b")]}garbage\r\n[1, 2, 3]";
368 let mut reader = io::BufReader::new(StripHeaderReader::new(input));
369 let mut text = String::new();
370 reader.read_line(&mut text).ok();
371 assert_eq!(text, "[1, 2, 3]");
372 }
373
374 #[test]
375 fn test_bad_newline() {
376 let input: &[_] = b")]}'\r[1, 2, 3]";
377 let mut reader = io::BufReader::new(StripHeaderReader::new(input));
378 let mut text = String::new();
379 match reader.read_line(&mut text) {
380 Err(err) => {
381 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
382 }
383 Ok(_) => {
384 panic!("Expected failure");
385 }
386 }
387 }
388
389 #[test]
390 fn test_decode_rmi() {
391 fn decode(rmi_str: &str) -> Vec<usize> {
392 let mut out = bitvec::bitvec![u8, Lsb0; 0; 0];
393 decode_rmi(rmi_str, &mut out).expect("failed to decode");
394
395 let mut res = vec![];
396 for (idx, bit) in out.iter().enumerate() {
397 if *bit {
398 res.push(idx);
399 }
400 }
401 res
402 }
403
404 assert_eq!(decode("AAB"), vec![12]);
406 assert_eq!(decode("g"), vec![5]);
407 assert_eq!(decode("Bg"), vec![0, 11]);
408 }
409
410 #[test]
411 fn test_decode_sourcemap_index_no_debug_id() {
412 let raw = RawSourceMap {
413 version: Some(3),
414 file: Some("test.js".into()),
415 sources: None,
416 source_root: None,
417 sources_content: None,
418 sections: Some(vec![]),
419 names: None,
420 scopes: None,
421 range_mappings: None,
422 mappings: None,
423 ignore_list: None,
424 x_facebook_offsets: None,
425 x_metro_module_paths: None,
426 x_facebook_sources: None,
427 debug_id: None,
428 _debug_id_new: None,
429 };
430
431 let decoded = decode_common(raw).expect("should decoded");
432 assert_eq!(
433 decoded,
434 DecodedMap::Index(SourceMapIndex::new(Some("test.js".into()), vec![]))
435 );
436 }
437
438 #[test]
439 fn test_decode_sourcemap_index_debug_id() {
440 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
441
442 let raw = RawSourceMap {
443 version: Some(3),
444 file: Some("test.js".into()),
445 sources: None,
446 source_root: None,
447 sources_content: None,
448 sections: Some(vec![]),
449 names: None,
450 scopes: None,
451 range_mappings: None,
452 mappings: None,
453 ignore_list: None,
454 x_facebook_offsets: None,
455 x_metro_module_paths: None,
456 x_facebook_sources: None,
457 debug_id: None,
458 _debug_id_new: Some(DEBUG_ID.parse().expect("valid debug id")),
459 };
460
461 let decoded = decode_common(raw).expect("should decode");
462 assert_eq!(
463 decoded,
464 DecodedMap::Index(
465 SourceMapIndex::new(Some("test.js".into()), vec![])
466 .with_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")))
467 )
468 );
469 }
470
471 #[test]
472 fn test_decode_sourcemap_index_debug_id_from_legacy_key() {
473 const DEBUG_ID: &str = "0123456789abcdef0123456789abcdef";
474
475 let raw = RawSourceMap {
476 version: Some(3),
477 file: Some("test.js".into()),
478 sources: None,
479 source_root: None,
480 sources_content: None,
481 sections: Some(vec![]),
482 names: None,
483 scopes: None,
484 range_mappings: None,
485 mappings: None,
486 ignore_list: None,
487 x_facebook_offsets: None,
488 x_metro_module_paths: None,
489 x_facebook_sources: None,
490 debug_id: Some(DEBUG_ID.parse().expect("valid debug id")),
491 _debug_id_new: None,
492 };
493
494 let decoded = decode_common(raw).expect("should decode");
495 assert_eq!(
496 decoded,
497 DecodedMap::Index(
498 SourceMapIndex::new(Some("test.js".into()), vec![])
499 .with_debug_id(Some(DEBUG_ID.parse().expect("valid debug id")))
500 )
501 );
502 }
503}