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