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