1use std::{
2 io::{Read, Write},
3 ops::{Deref, DerefMut},
4};
5
6use bytes_str::BytesStr;
7
8use crate::{
9 decoder::{decode, decode_regular, decode_slice},
10 encoder::{encode, Encodable},
11 errors::{Error, Result},
12 jsontypes::{FacebookScopeMapping, FacebookSources, RawSourceMap},
13 types::{DecodedMap, RewriteOptions, SourceMap},
14 utils::greatest_lower_bound,
15 vlq::parse_vlq_segment_into,
16 Token,
17};
18
19#[derive(Debug, Clone, PartialEq)]
23pub struct HermesScopeOffset {
24 line: u32,
25 column: u32,
26 name_index: u32,
27}
28
29#[derive(Debug, Clone, PartialEq)]
30pub struct HermesFunctionMap {
31 names: Vec<BytesStr>,
32 mappings: Vec<HermesScopeOffset>,
33}
34
35#[derive(Debug, Clone, PartialEq)]
38pub struct SourceMapHermes {
39 pub(crate) sm: SourceMap,
40 function_maps: Vec<Option<HermesFunctionMap>>,
42 raw_facebook_sources: FacebookSources,
45}
46
47impl Deref for SourceMapHermes {
48 type Target = SourceMap;
49
50 fn deref(&self) -> &Self::Target {
51 &self.sm
52 }
53}
54
55impl DerefMut for SourceMapHermes {
56 fn deref_mut(&mut self) -> &mut Self::Target {
57 &mut self.sm
58 }
59}
60
61impl Encodable for SourceMapHermes {
62 fn as_raw_sourcemap(&self) -> RawSourceMap {
63 let mut rsm = self.sm.as_raw_sourcemap();
65 rsm.x_facebook_sources
66 .clone_from(&self.raw_facebook_sources);
67 rsm
68 }
69}
70
71impl SourceMapHermes {
72 pub fn from_reader<R: Read>(rdr: R) -> Result<Self> {
77 match decode(rdr)? {
78 DecodedMap::Hermes(sm) => Ok(sm),
79 _ => Err(Error::IncompatibleSourceMap),
80 }
81 }
82
83 pub fn from_slice(slice: &[u8]) -> Result<Self> {
88 match decode_slice(slice)? {
89 DecodedMap::Hermes(sm) => Ok(sm),
90 _ => Err(Error::IncompatibleSourceMap),
91 }
92 }
93
94 pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
98 encode(self, w)
99 }
100
101 pub fn get_original_function_name(&self, bytecode_offset: u32) -> Option<&BytesStr> {
104 let token = self.sm.lookup_token(0, bytecode_offset)?;
105
106 self.get_scope_for_token(token)
107 }
108
109 pub fn get_scope_for_token(&self, token: Token) -> Option<&BytesStr> {
111 let function_map = self
112 .function_maps
113 .get(token.get_src_id() as usize)?
114 .as_ref()?;
115
116 let (_mapping_idx, mapping) = greatest_lower_bound(
121 &function_map.mappings,
122 &(token.get_src_line() + 1, token.get_src_col()),
123 |o| (o.line, o.column),
124 )?;
125 function_map.names.get(mapping.name_index as usize)
126 }
127
128 pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<Self> {
133 let Self {
134 sm,
135 mut function_maps,
136 mut raw_facebook_sources,
137 } = self;
138
139 let (sm, mapping) = sm.rewrite_with_mapping(options)?;
140
141 if function_maps.len() >= mapping.len() {
142 function_maps = mapping
143 .iter()
144 .map(|idx| function_maps[*idx as usize].take())
145 .collect();
146 raw_facebook_sources = raw_facebook_sources.map(|mut sources| {
147 mapping
148 .into_iter()
149 .map(|idx| sources[idx as usize].take())
150 .collect()
151 });
152 }
153
154 Ok(Self {
155 sm,
156 function_maps,
157 raw_facebook_sources,
158 })
159 }
160}
161
162pub fn decode_hermes(mut rsm: RawSourceMap) -> Result<SourceMapHermes> {
163 let x_facebook_sources = rsm
164 .x_facebook_sources
165 .take()
166 .ok_or(Error::IncompatibleSourceMap)?;
167
168 let mut nums = Vec::with_capacity(4);
172
173 let function_maps = x_facebook_sources
174 .iter()
175 .map(|v| {
176 let FacebookScopeMapping {
177 names,
178 mappings: raw_mappings,
179 } = v.as_ref()?.iter().next()?;
180
181 let mut mappings = vec![];
182 let mut line = 1;
183 let mut name_index = 0;
184
185 for line_mapping in raw_mappings.split(';') {
186 if line_mapping.is_empty() {
187 continue;
188 }
189
190 let mut column = 0;
191
192 for mapping in line_mapping.split(',') {
193 if mapping.is_empty() {
194 continue;
195 }
196
197 nums.clear();
198 parse_vlq_segment_into(mapping, &mut nums).ok()?;
199 let mut nums = nums.iter().copied();
200
201 column = (i64::from(column) + nums.next()?) as u32;
202 name_index = (i64::from(name_index) + nums.next().unwrap_or(0)) as u32;
203 line = (i64::from(line) + nums.next().unwrap_or(0)) as u32;
204 mappings.push(HermesScopeOffset {
205 column,
206 line,
207 name_index,
208 });
209 }
210 }
211 Some(HermesFunctionMap {
212 names: names.clone(),
213 mappings,
214 })
215 })
216 .collect();
217
218 let sm = decode_regular(rsm)?;
219 Ok(SourceMapHermes {
220 sm,
221 function_maps,
222 raw_facebook_sources: Some(x_facebook_sources),
223 })
224}