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