From 63f4d4cb1f1b9847e3330b2ede5a15915fa794b5 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 20 Dec 2022 17:33:20 +0100 Subject: [PATCH] fix: Correctly rewrite SourceMapHermes --- src/builder.rs | 32 +++++++++++++++++++++++++++++--- src/decoder.rs | 2 +- src/hermes.rs | 26 ++++++++++++++++++++++---- src/types.rs | 14 +++++++++++++- tests/test_hermes.rs | 5 +++++ 5 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 888e2baf..bcfc5ab3 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -26,6 +26,7 @@ pub struct SourceMapBuilder { source_root: Option, sources: Vec, source_contents: Vec>, + sources_mapping: Vec, } #[cfg(any(unix, windows, target_os = "redox"))] @@ -57,6 +58,7 @@ impl SourceMapBuilder { source_root: None, sources: vec![], source_contents: vec![], + sources_mapping: vec![], } } @@ -82,10 +84,15 @@ impl SourceMapBuilder { /// Registers a new source with the builder and returns the source ID. pub fn add_source(&mut self, src: &str) -> u32 { + self.add_source_with_id(src, !0) + } + + fn add_source_with_id(&mut self, src: &str, old_id: u32) -> u32 { let count = self.sources.len() as u32; let id = *self.source_map.entry(src.into()).or_insert(count); if id == count { self.sources.push(src.into()); + self.sources_mapping.push(old_id); } id } @@ -144,7 +151,7 @@ impl SourceMapBuilder { let rv = to_read.len(); for (src_id, path) in to_read { - if let Ok(mut f) = fs::File::open(&path) { + if let Ok(mut f) = fs::File::open(path) { let mut contents = String::new(); if f.read_to_string(&mut contents).is_ok() { self.set_source_contents(src_id, Some(&contents)); @@ -174,9 +181,23 @@ impl SourceMapBuilder { src_col: u32, source: Option<&str>, name: Option<&str>, + ) -> RawToken { + self.add_with_id(dst_line, dst_col, src_line, src_col, source, !0, name) + } + + #[allow(clippy::too_many_arguments)] + fn add_with_id( + &mut self, + dst_line: u32, + dst_col: u32, + src_line: u32, + src_col: u32, + source: Option<&str>, + source_id: u32, + name: Option<&str>, ) -> RawToken { let src_id = match source { - Some(source) => self.add_source(source), + Some(source) => self.add_source_with_id(source, source_id), None => !0, }; let name_id = match name { @@ -223,12 +244,13 @@ impl SourceMapBuilder { /// optionally removing the name. pub fn add_token(&mut self, token: &Token<'_>, with_name: bool) -> RawToken { let name = if with_name { token.get_name() } else { None }; - self.add( + self.add_with_id( token.get_dst_line(), token.get_dst_col(), token.get_src_line(), token.get_src_col(), token.get_source(), + token.get_src_id(), name, ) } @@ -249,6 +271,10 @@ impl SourceMapBuilder { } } + pub(crate) fn take_mapping(&mut self) -> Vec { + std::mem::take(&mut self.sources_mapping) + } + /// Converts the builder into a sourcemap. pub fn into_sourcemap(self) -> SourceMap { let contents = if !self.source_contents.is_empty() { diff --git a/src/decoder.rs b/src/decoder.rs index 3c010d81..4ad3faeb 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -172,7 +172,7 @@ pub fn decode_regular(rsm: RawSourceMap) -> Result { if name_id >= names.len() as u32 { fail!(Error::BadNameReference(name_id)); } - name = name_id as u32; + name = name_id; } } diff --git a/src/hermes.rs b/src/hermes.rs index 6d2188d1..55cca4f8 100644 --- a/src/hermes.rs +++ b/src/hermes.rs @@ -100,7 +100,10 @@ impl SourceMapHermes { /// Resolves the name of the enclosing function for the given [`Token`]. pub fn get_scope_for_token(&self, token: Token) -> Option<&str> { - let function_map = (self.function_maps.get(token.get_src_id() as usize))?.as_ref()?; + let function_map = self + .function_maps + .get(token.get_src_id() as usize)? + .as_ref()?; // Find the closest mapping, just like here: // https://github.com/facebook/metro/blob/63b523eb20e7bdf62018aeaf195bb5a3a1a67f36/packages/metro-symbolicate/src/SourceMetadataMapConsumer.js#L204-L231 @@ -120,10 +123,25 @@ impl SourceMapHermes { pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result { let Self { sm, - function_maps, - raw_facebook_sources, + mut function_maps, + mut raw_facebook_sources, } = self; - let sm = sm.rewrite(options)?; + + let (sm, mapping) = sm.rewrite_with_mapping(options)?; + + if function_maps.len() >= mapping.len() { + function_maps = mapping + .iter() + .map(|idx| function_maps[*idx as usize].take()) + .collect(); + raw_facebook_sources = raw_facebook_sources.map(|mut sources| { + mapping + .into_iter() + .map(|idx| sources[idx as usize].take()) + .collect() + }); + } + Ok(Self { sm, function_maps, diff --git a/src/types.rs b/src/types.rs index 76bce2bc..a7067f50 100644 --- a/src/types.rs +++ b/src/types.rs @@ -757,6 +757,14 @@ impl SourceMap { /// }); /// ``` pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result { + Ok(self.rewrite_with_mapping(options)?.0) + } + + /// Same as `rewrite`, except also returns a remapping index for deduplicated `sources`. + pub(crate) fn rewrite_with_mapping( + self, + options: &RewriteOptions<'_>, + ) -> Result<(SourceMap, Vec)> { let mut builder = SourceMapBuilder::new(self.get_file()); for token in self.tokens() { @@ -795,7 +803,11 @@ impl SourceMap { builder.strip_prefixes(&prefixes); } - Ok(builder.into_sourcemap()) + let mapping = builder.take_mapping(); + + let sm = builder.into_sourcemap(); + + Ok((sm, mapping)) } } diff --git a/tests/test_hermes.rs b/tests/test_hermes.rs index 2477c35e..37242f85 100644 --- a/tests/test_hermes.rs +++ b/tests/test_hermes.rs @@ -4,6 +4,7 @@ use sourcemap::SourceMapHermes; fn test_react_native_hermes() { let input = include_bytes!("./fixtures/react-native-hermes/output.map"); let sm = SourceMapHermes::from_reader(&input[..]).unwrap(); + let sm = sm.rewrite(&Default::default()).unwrap(); // at foo (address at unknown:1:11939) assert_eq!( @@ -24,6 +25,10 @@ fn test_react_native_hermes() { fn test_react_native_metro() { let input = include_bytes!("./fixtures/react-native-metro/output.js.map"); let sm = SourceMapHermes::from_reader(&input[..]).unwrap(); + // The given map has a bogus `__prelude__` first source, which is being + // dropped (as its not referenced) by rewriting the sourcemap, and thus + // the internal hermes mappings also need to be rewritten accordingly + let sm = sm.rewrite(&Default::default()).unwrap(); // at foo (output.js:1289:11) let token = sm.lookup_token(1288, 10).unwrap();