1use srcmap_codec::{vlq_decode, vlq_decode_unsigned};
6
7use crate::{
8 Binding, CallSite, GeneratedRange, OriginalScope, Position, ScopeInfo, ScopesError,
9 SubRangeBinding, TAG_GENERATED_RANGE_BINDINGS, TAG_GENERATED_RANGE_CALL_SITE,
10 TAG_GENERATED_RANGE_END, TAG_GENERATED_RANGE_START, TAG_GENERATED_RANGE_SUB_RANGE_BINDINGS,
11 TAG_ORIGINAL_SCOPE_END, TAG_ORIGINAL_SCOPE_START, TAG_ORIGINAL_SCOPE_VARIABLES,
12 resolve_binding, resolve_name,
13};
14
15struct Tokenizer<'a> {
18 input: &'a [u8],
19 pos: usize,
20}
21
22impl<'a> Tokenizer<'a> {
23 fn new(input: &'a [u8]) -> Self {
24 Self { input, pos: 0 }
25 }
26
27 fn has_next(&self) -> bool {
28 self.pos < self.input.len()
29 }
30
31 fn at_item_end(&self) -> bool {
33 self.pos >= self.input.len() || self.input[self.pos] == b','
34 }
35
36 fn skip_comma(&mut self) {
38 if self.pos < self.input.len() && self.input[self.pos] == b',' {
39 self.pos += 1;
40 }
41 }
42
43 fn read_unsigned(&mut self) -> Result<u64, ScopesError> {
44 let (val, consumed) = vlq_decode_unsigned(self.input, self.pos)?;
45 self.pos += consumed;
46 Ok(val)
47 }
48
49 fn read_signed(&mut self) -> Result<i64, ScopesError> {
50 let (val, consumed) = vlq_decode(self.input, self.pos)?;
51 self.pos += consumed;
52 Ok(val)
53 }
54}
55
56struct BuildingScope {
59 start: Position,
60 name: Option<String>,
61 kind: Option<String>,
62 is_stack_frame: bool,
63 variables: Vec<String>,
64 children: Vec<OriginalScope>,
65}
66
67struct BuildingRange {
68 start: Position,
69 is_stack_frame: bool,
70 is_hidden: bool,
71 definition: Option<usize>,
72 call_site: Option<CallSite>,
73 bindings: Vec<Binding>,
74 sub_range_bindings: Vec<(usize, Vec<SubRangeBinding>)>,
75 children: Vec<GeneratedRange>,
76}
77
78pub fn decode_scopes(
86 input: &str,
87 names: &[String],
88 num_sources: usize,
89) -> Result<ScopeInfo, ScopesError> {
90 if input.is_empty() {
91 let scopes = vec![None; num_sources];
92 return Ok(ScopeInfo {
93 scopes,
94 ranges: vec![],
95 });
96 }
97
98 let mut tok = Tokenizer::new(input.as_bytes());
99
100 let mut scopes: Vec<Option<OriginalScope>> = Vec::new();
102 let mut source_idx = 0usize;
103 let mut scope_stack: Vec<BuildingScope> = Vec::new();
104 let mut os_line = 0u32;
105 let mut os_col = 0u32;
106 let mut os_name = 0i64;
107 let mut os_kind = 0i64;
108 let mut os_var = 0i64;
109
110 let mut ranges: Vec<GeneratedRange> = Vec::new();
112 let mut range_stack: Vec<BuildingRange> = Vec::new();
113 let mut gr_line = 0u32;
114 let mut gr_col = 0u32;
115 let mut gr_def = 0i64;
116 let mut in_generated_ranges = false;
117
118 while tok.has_next() {
119 if tok.at_item_end() {
121 if !in_generated_ranges && source_idx < num_sources && scope_stack.is_empty() {
122 scopes.push(None);
123 source_idx += 1;
124 }
125 tok.skip_comma();
126 continue;
127 }
128
129 let tag = tok.read_unsigned()?;
130
131 match tag {
132 TAG_ORIGINAL_SCOPE_START => {
133 if scope_stack.is_empty() {
135 os_line = 0;
136 os_col = 0;
137 }
138
139 let flags = tok.read_unsigned()?;
140
141 let line_delta = tok.read_unsigned()? as u32;
142 os_line += line_delta;
143 let col_raw = tok.read_unsigned()? as u32;
144 os_col = if line_delta != 0 {
145 col_raw
146 } else {
147 os_col + col_raw
148 };
149
150 let name = if flags & crate::OS_FLAG_HAS_NAME != 0 {
151 let d = tok.read_signed()?;
152 os_name += d;
153 Some(resolve_name(names, os_name)?)
154 } else {
155 None
156 };
157
158 let kind = if flags & crate::OS_FLAG_HAS_KIND != 0 {
159 let d = tok.read_signed()?;
160 os_kind += d;
161 Some(resolve_name(names, os_kind)?)
162 } else {
163 None
164 };
165
166 let is_stack_frame = flags & crate::OS_FLAG_IS_STACK_FRAME != 0;
167
168 scope_stack.push(BuildingScope {
169 start: Position {
170 line: os_line,
171 column: os_col,
172 },
173 name,
174 kind,
175 is_stack_frame,
176 variables: Vec::new(),
177 children: Vec::new(),
178 });
179 }
180
181 TAG_ORIGINAL_SCOPE_END => {
182 if scope_stack.is_empty() {
183 return Err(ScopesError::UnmatchedScopeEnd);
184 }
185
186 let line_delta = tok.read_unsigned()? as u32;
187 os_line += line_delta;
188 let col_raw = tok.read_unsigned()? as u32;
189 os_col = if line_delta != 0 {
190 col_raw
191 } else {
192 os_col + col_raw
193 };
194
195 let building = scope_stack.pop().unwrap();
196 let finished = OriginalScope {
197 start: building.start,
198 end: Position {
199 line: os_line,
200 column: os_col,
201 },
202 name: building.name,
203 kind: building.kind,
204 is_stack_frame: building.is_stack_frame,
205 variables: building.variables,
206 children: building.children,
207 };
208
209 if scope_stack.is_empty() {
210 scopes.push(Some(finished));
211 source_idx += 1;
212 } else {
214 scope_stack.last_mut().unwrap().children.push(finished);
215 }
216 }
217
218 TAG_ORIGINAL_SCOPE_VARIABLES => {
219 if let Some(current) = scope_stack.last_mut() {
220 while !tok.at_item_end() {
221 let d = tok.read_signed()?;
222 os_var += d;
223 current.variables.push(resolve_name(names, os_var)?);
224 }
225 }
226 }
227
228 TAG_GENERATED_RANGE_START => {
229 if !in_generated_ranges {
230 in_generated_ranges = true;
231 while scopes.len() < num_sources {
233 scopes.push(None);
234 }
235 source_idx = num_sources;
236 }
237
238 let flags = tok.read_unsigned()?;
239
240 let line_delta = if flags & crate::GR_FLAG_HAS_LINE != 0 {
241 tok.read_unsigned()? as u32
242 } else {
243 0
244 };
245 gr_line += line_delta;
246
247 let col_raw = tok.read_unsigned()? as u32;
248 gr_col = if line_delta != 0 {
249 col_raw
250 } else {
251 gr_col + col_raw
252 };
253
254 let definition = if flags & crate::GR_FLAG_HAS_DEFINITION != 0 {
255 let d = tok.read_signed()?;
256 gr_def += d;
257 Some(gr_def as usize)
258 } else {
259 None
260 };
261
262 let is_stack_frame = flags & crate::GR_FLAG_IS_STACK_FRAME != 0;
263 let is_hidden = flags & crate::GR_FLAG_IS_HIDDEN != 0;
264
265 range_stack.push(BuildingRange {
266 start: Position {
267 line: gr_line,
268 column: gr_col,
269 },
270 is_stack_frame,
271 is_hidden,
272 definition,
273 call_site: None,
274 bindings: Vec::new(),
275 sub_range_bindings: Vec::new(),
276 children: Vec::new(),
277 });
278 }
279
280 TAG_GENERATED_RANGE_END => {
281 if range_stack.is_empty() {
282 return Err(ScopesError::UnmatchedRangeEnd);
283 }
284
285 let first = tok.read_unsigned()? as u32;
287 let (line_delta, col_raw) = if !tok.at_item_end() {
288 let second = tok.read_unsigned()? as u32;
289 (first, second)
290 } else {
291 (0, first)
292 };
293 gr_line += line_delta;
294 gr_col = if line_delta != 0 {
295 col_raw
296 } else {
297 gr_col + col_raw
298 };
299
300 let building = range_stack.pop().unwrap();
301
302 let final_bindings = merge_bindings(
304 building.bindings,
305 &building.sub_range_bindings,
306 building.start,
307 );
308
309 let finished = GeneratedRange {
310 start: building.start,
311 end: Position {
312 line: gr_line,
313 column: gr_col,
314 },
315 is_stack_frame: building.is_stack_frame,
316 is_hidden: building.is_hidden,
317 definition: building.definition,
318 call_site: building.call_site,
319 bindings: final_bindings,
320 children: building.children,
321 };
322
323 if range_stack.is_empty() {
324 ranges.push(finished);
325 } else {
326 range_stack.last_mut().unwrap().children.push(finished);
327 }
328 }
329
330 TAG_GENERATED_RANGE_BINDINGS => {
331 if let Some(current) = range_stack.last_mut() {
332 while !tok.at_item_end() {
333 let idx = tok.read_unsigned()?;
334 let binding = match resolve_binding(names, idx)? {
335 Some(expr) => Binding::Expression(expr),
336 None => Binding::Unavailable,
337 };
338 current.bindings.push(binding);
339 }
340 }
341 }
342
343 TAG_GENERATED_RANGE_SUB_RANGE_BINDINGS => {
344 if let Some(current) = range_stack.last_mut() {
345 let var_idx = tok.read_unsigned()? as usize;
346
347 let mut sub_ranges: Vec<SubRangeBinding> = Vec::new();
348 let mut h_line = current.start.line;
350 let mut h_col = current.start.column;
351
352 while !tok.at_item_end() {
353 let binding_raw = tok.read_unsigned()?;
354 let line_delta = tok.read_unsigned()? as u32;
355 h_line += line_delta;
356
357 let col_raw = tok.read_unsigned()? as u32;
358 h_col = if line_delta != 0 {
359 col_raw
360 } else {
361 h_col + col_raw
362 };
363
364 let expression = resolve_binding(names, binding_raw)?;
365 sub_ranges.push(SubRangeBinding {
366 expression,
367 from: Position {
368 line: h_line,
369 column: h_col,
370 },
371 });
372 }
373
374 current.sub_range_bindings.push((var_idx, sub_ranges));
375 }
376 }
377
378 TAG_GENERATED_RANGE_CALL_SITE => {
379 if let Some(current) = range_stack.last_mut() {
380 let source_index = tok.read_unsigned()? as u32;
381 let line = tok.read_unsigned()? as u32;
382 let column = tok.read_unsigned()? as u32;
383 current.call_site = Some(CallSite {
384 source_index,
385 line,
386 column,
387 });
388 }
389 }
390
391 _ => {
392 while !tok.at_item_end() {
394 let _ = tok.read_unsigned()?;
395 }
396 }
397 }
398
399 tok.skip_comma();
400 }
401
402 if !scope_stack.is_empty() {
403 return Err(ScopesError::UnclosedScope);
404 }
405 if !range_stack.is_empty() {
406 return Err(ScopesError::UnclosedRange);
407 }
408
409 while scopes.len() < num_sources {
411 scopes.push(None);
412 }
413
414 Ok(ScopeInfo { scopes, ranges })
415}
416
417fn merge_bindings(
419 initial: Vec<Binding>,
420 sub_range_map: &[(usize, Vec<SubRangeBinding>)],
421 range_start: Position,
422) -> Vec<Binding> {
423 if sub_range_map.is_empty() {
424 return initial;
425 }
426
427 let mut result = initial;
428
429 for (var_idx, sub_ranges) in sub_range_map {
430 if *var_idx < result.len() {
431 let initial_expr = match &result[*var_idx] {
433 Binding::Expression(e) => Some(e.clone()),
434 Binding::Unavailable => None,
435 Binding::SubRanges(_) => None, };
437
438 let mut all_subs = vec![SubRangeBinding {
439 expression: initial_expr,
440 from: range_start,
441 }];
442 all_subs.extend(sub_ranges.iter().cloned());
443
444 result[*var_idx] = Binding::SubRanges(all_subs);
445 }
446 }
447
448 result
449}