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
83struct DecodeState {
84 scopes: Vec<Option<OriginalScope>>,
85 source_idx: usize,
86 scope_stack: Vec<BuildingScope>,
87 os_line: u32,
88 os_col: u32,
89 os_name: i64,
90 os_kind: i64,
91 os_var: i64,
92 ranges: Vec<GeneratedRange>,
93 range_stack: Vec<BuildingRange>,
94 gr_line: u32,
95 gr_col: u32,
96 gr_def: i64,
97 h_var_acc: u64,
98 in_generated_ranges: bool,
99 num_sources: usize,
100}
101
102impl DecodeState {
103 fn new(num_sources: usize) -> Self {
104 Self {
105 scopes: Vec::new(),
106 source_idx: 0,
107 scope_stack: Vec::new(),
108 os_line: 0,
109 os_col: 0,
110 os_name: 0,
111 os_kind: 0,
112 os_var: 0,
113 ranges: Vec::new(),
114 range_stack: Vec::new(),
115 gr_line: 0,
116 gr_col: 0,
117 gr_def: 0,
118 h_var_acc: 0,
119 in_generated_ranges: false,
120 num_sources,
121 }
122 }
123
124 fn finish(mut self) -> Result<ScopeInfo, ScopesError> {
125 if !self.scope_stack.is_empty() {
126 return Err(ScopesError::UnclosedScope);
127 }
128 if !self.range_stack.is_empty() {
129 return Err(ScopesError::UnclosedRange);
130 }
131
132 while self.scopes.len() < self.num_sources {
134 self.scopes.push(None);
135 }
136
137 Ok(ScopeInfo { scopes: self.scopes, ranges: self.ranges })
138 }
139
140 fn handle_empty_item(&mut self) {
141 if !self.in_generated_ranges
142 && self.source_idx < self.num_sources
143 && self.scope_stack.is_empty()
144 {
145 self.scopes.push(None);
146 self.source_idx += 1;
147 }
148 }
149
150 fn start_generated_ranges_if_needed(&mut self) {
151 if self.in_generated_ranges {
152 return;
153 }
154
155 self.in_generated_ranges = true;
156 while self.scopes.len() < self.num_sources {
158 self.scopes.push(None);
159 }
160 self.source_idx = self.num_sources;
161 }
162
163 fn handle_original_scope_start(
164 &mut self,
165 tok: &mut Tokenizer<'_>,
166 names: &[String],
167 ) -> Result<(), ScopesError> {
168 if self.scope_stack.is_empty() {
169 self.os_line = 0;
170 self.os_col = 0;
171 }
172
173 let flags = tok.read_unsigned()?;
174
175 let line_delta = tok.read_unsigned()? as u32;
176 Self::advance_position(
177 line_delta,
178 tok.read_unsigned()? as u32,
179 &mut self.os_line,
180 &mut self.os_col,
181 );
182
183 let name = if flags & crate::OS_FLAG_HAS_NAME != 0 {
184 let d = tok.read_signed()?;
185 self.os_name += d;
186 Some(resolve_name(names, self.os_name)?)
187 } else {
188 None
189 };
190
191 let kind = if flags & crate::OS_FLAG_HAS_KIND != 0 {
192 let d = tok.read_signed()?;
193 self.os_kind += d;
194 Some(resolve_name(names, self.os_kind)?)
195 } else {
196 None
197 };
198
199 let is_stack_frame = flags & crate::OS_FLAG_IS_STACK_FRAME != 0;
200
201 self.scope_stack.push(BuildingScope {
202 start: Position { line: self.os_line, column: self.os_col },
203 name,
204 kind,
205 is_stack_frame,
206 variables: Vec::new(),
207 children: Vec::new(),
208 });
209
210 Ok(())
211 }
212
213 fn handle_original_scope_end(&mut self, tok: &mut Tokenizer<'_>) -> Result<(), ScopesError> {
214 if self.scope_stack.is_empty() {
215 return Err(ScopesError::UnmatchedScopeEnd);
216 }
217
218 let line_delta = tok.read_unsigned()? as u32;
219 Self::advance_position(
220 line_delta,
221 tok.read_unsigned()? as u32,
222 &mut self.os_line,
223 &mut self.os_col,
224 );
225
226 let building = self.scope_stack.pop().expect("non-empty: checked above");
228 let finished = OriginalScope {
229 start: building.start,
230 end: Position { line: self.os_line, column: self.os_col },
231 name: building.name,
232 kind: building.kind,
233 is_stack_frame: building.is_stack_frame,
234 variables: building.variables,
235 children: building.children,
236 };
237
238 if self.scope_stack.is_empty() {
239 self.scopes.push(Some(finished));
240 self.source_idx += 1;
241 } else {
242 self.scope_stack.last_mut().expect("non-empty: checked above").children.push(finished);
243 }
244
245 Ok(())
246 }
247
248 fn handle_original_scope_variables(
249 &mut self,
250 tok: &mut Tokenizer<'_>,
251 names: &[String],
252 ) -> Result<(), ScopesError> {
253 if let Some(current) = self.scope_stack.last_mut() {
254 while !tok.at_item_end() {
255 let d = tok.read_signed()?;
256 self.os_var += d;
257 current.variables.push(resolve_name(names, self.os_var)?);
258 }
259 } else {
260 while !tok.at_item_end() {
261 let _ = tok.read_signed()?;
262 }
263 }
264
265 Ok(())
266 }
267
268 fn handle_generated_range_start(&mut self, tok: &mut Tokenizer<'_>) -> Result<(), ScopesError> {
269 self.start_generated_ranges_if_needed();
270
271 let flags = tok.read_unsigned()?;
272
273 let line_delta =
274 if flags & crate::GR_FLAG_HAS_LINE != 0 { tok.read_unsigned()? as u32 } else { 0 };
275 Self::advance_position(
276 line_delta,
277 tok.read_unsigned()? as u32,
278 &mut self.gr_line,
279 &mut self.gr_col,
280 );
281
282 let definition = if flags & crate::GR_FLAG_HAS_DEFINITION != 0 {
283 let d = tok.read_signed()?;
284 self.gr_def += d;
285 Some(self.gr_def as u32)
286 } else {
287 None
288 };
289
290 let is_stack_frame = flags & crate::GR_FLAG_IS_STACK_FRAME != 0;
291 let is_hidden = flags & crate::GR_FLAG_IS_HIDDEN != 0;
292
293 self.h_var_acc = 0;
295
296 self.range_stack.push(BuildingRange {
297 start: Position { line: self.gr_line, column: self.gr_col },
298 is_stack_frame,
299 is_hidden,
300 definition,
301 call_site: None,
302 bindings: Vec::new(),
303 sub_range_bindings: Vec::new(),
304 children: Vec::new(),
305 });
306
307 Ok(())
308 }
309
310 fn handle_generated_range_end(&mut self, tok: &mut Tokenizer<'_>) -> Result<(), ScopesError> {
311 if self.range_stack.is_empty() {
312 return Err(ScopesError::UnmatchedRangeEnd);
313 }
314
315 let first = tok.read_unsigned()? as u32;
317 let (line_delta, col_raw) = if !tok.at_item_end() {
318 let second = tok.read_unsigned()? as u32;
319 (first, second)
320 } else {
321 (0, first)
322 };
323 Self::advance_position(line_delta, col_raw, &mut self.gr_line, &mut self.gr_col);
324
325 let building = self.range_stack.pop().expect("non-empty: checked above");
327
328 let final_bindings =
330 merge_bindings(building.bindings, &building.sub_range_bindings, building.start);
331
332 let finished = GeneratedRange {
333 start: building.start,
334 end: Position { line: self.gr_line, column: self.gr_col },
335 is_stack_frame: building.is_stack_frame,
336 is_hidden: building.is_hidden,
337 definition: building.definition,
338 call_site: building.call_site,
339 bindings: final_bindings,
340 children: building.children,
341 };
342
343 if self.range_stack.is_empty() {
344 self.ranges.push(finished);
345 } else {
346 self.range_stack.last_mut().expect("non-empty: checked above").children.push(finished);
347 }
348
349 Ok(())
350 }
351
352 fn handle_generated_range_bindings(
353 &mut self,
354 tok: &mut Tokenizer<'_>,
355 names: &[String],
356 ) -> Result<(), ScopesError> {
357 if let Some(current) = self.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 Self::skip_unsigned_item(tok)?;
368 }
369
370 Ok(())
371 }
372
373 fn handle_generated_range_sub_range_bindings(
374 &mut self,
375 tok: &mut Tokenizer<'_>,
376 names: &[String],
377 ) -> Result<(), ScopesError> {
378 if let Some(current) = self.range_stack.last_mut() {
379 let var_delta = tok.read_unsigned()?;
380 self.h_var_acc += var_delta;
381 let var_idx = self.h_var_acc as usize;
382
383 let mut sub_ranges: Vec<SubRangeBinding> = Vec::new();
384 let mut h_line = current.start.line;
386 let mut h_col = current.start.column;
387
388 while !tok.at_item_end() {
389 let binding_raw = tok.read_unsigned()?;
390 let line_delta = tok.read_unsigned()? as u32;
391 Self::advance_position(
392 line_delta,
393 tok.read_unsigned()? as u32,
394 &mut h_line,
395 &mut h_col,
396 );
397
398 let expression = resolve_binding(names, binding_raw)?;
399 sub_ranges.push(SubRangeBinding {
400 expression,
401 from: Position { line: h_line, column: h_col },
402 });
403 }
404
405 current.sub_range_bindings.push((var_idx, sub_ranges));
406 } else {
407 Self::skip_unsigned_item(tok)?;
408 }
409
410 Ok(())
411 }
412
413 fn handle_generated_range_call_site(
414 &mut self,
415 tok: &mut Tokenizer<'_>,
416 ) -> Result<(), ScopesError> {
417 if let Some(current) = self.range_stack.last_mut() {
418 let source_index = tok.read_unsigned()? as u32;
419 let line = tok.read_unsigned()? as u32;
420 let column = tok.read_unsigned()? as u32;
421 current.call_site = Some(CallSite { source_index, line, column });
422 } else {
423 Self::skip_unsigned_item(tok)?;
424 }
425
426 Ok(())
427 }
428
429 fn advance_position(line_delta: u32, col_raw: u32, line: &mut u32, column: &mut u32) {
430 *line += line_delta;
431 *column = if line_delta != 0 { col_raw } else { *column + col_raw };
432 }
433
434 fn skip_unsigned_item(tok: &mut Tokenizer<'_>) -> Result<(), ScopesError> {
435 while !tok.at_item_end() {
436 let _ = tok.read_unsigned()?;
437 }
438 Ok(())
439 }
440}
441
442pub fn decode_scopes(
452 input: &str,
453 names: &[String],
454 num_sources: usize,
455) -> Result<ScopeInfo, ScopesError> {
456 let mut tok = Tokenizer::new(input.as_bytes());
457 let mut state = DecodeState::new(num_sources);
458
459 while tok.has_next() {
460 if tok.at_item_end() {
461 state.handle_empty_item();
462 tok.skip_comma();
463 continue;
464 }
465
466 let tag = tok.read_unsigned()?;
467
468 match tag {
469 TAG_ORIGINAL_SCOPE_START => state.handle_original_scope_start(&mut tok, names)?,
470 TAG_ORIGINAL_SCOPE_END => state.handle_original_scope_end(&mut tok)?,
471 TAG_ORIGINAL_SCOPE_VARIABLES => {
472 state.handle_original_scope_variables(&mut tok, names)?
473 }
474 TAG_GENERATED_RANGE_START => state.handle_generated_range_start(&mut tok)?,
475 TAG_GENERATED_RANGE_END => state.handle_generated_range_end(&mut tok)?,
476 TAG_GENERATED_RANGE_BINDINGS => {
477 state.handle_generated_range_bindings(&mut tok, names)?
478 }
479 TAG_GENERATED_RANGE_SUB_RANGE_BINDINGS => {
480 state.handle_generated_range_sub_range_bindings(&mut tok, names)?
481 }
482 TAG_GENERATED_RANGE_CALL_SITE => state.handle_generated_range_call_site(&mut tok)?,
483 _ => DecodeState::skip_unsigned_item(&mut tok)?,
484 }
485
486 tok.skip_comma();
487 }
488
489 state.finish()
490}
491
492fn merge_bindings(
494 initial: Vec<Binding>,
495 sub_range_map: &[(usize, Vec<SubRangeBinding>)],
496 range_start: Position,
497) -> Vec<Binding> {
498 if sub_range_map.is_empty() {
499 return initial;
500 }
501
502 let mut result = initial;
503
504 for (var_idx, sub_ranges) in sub_range_map {
505 if *var_idx < result.len() {
506 let initial_expr = match &result[*var_idx] {
508 Binding::Expression(e) => Some(e.clone()),
509 Binding::Unavailable | Binding::SubRanges(_) => None, };
511
512 let mut all_subs =
513 vec![SubRangeBinding { expression: initial_expr, from: range_start }];
514 all_subs.extend(sub_ranges.iter().cloned());
515
516 result[*var_idx] = Binding::SubRanges(all_subs);
517 }
518 }
519
520 result
521}