1use std::collections::HashMap;
6
7use srcmap_codec::{vlq_encode, vlq_encode_unsigned};
8
9use crate::{
10 Binding, GeneratedRange, OriginalScope, ScopeInfo, TAG_GENERATED_RANGE_BINDINGS,
11 TAG_GENERATED_RANGE_CALL_SITE, TAG_GENERATED_RANGE_END, TAG_GENERATED_RANGE_START,
12 TAG_GENERATED_RANGE_SUB_RANGE_BINDINGS, TAG_ORIGINAL_SCOPE_END, TAG_ORIGINAL_SCOPE_START,
13 TAG_ORIGINAL_SCOPE_VARIABLES, resolve_or_add_name,
14};
15
16struct ScopesEncoder<'a> {
19 output: Vec<u8>,
20 names: &'a mut Vec<String>,
21 name_map: HashMap<String, u32>,
22 first_item: bool,
23
24 os_line: u32,
26 os_col: u32,
27 os_name: i64,
28 os_kind: i64,
29 os_var: i64,
30
31 gr_line: u32,
33 gr_col: u32,
34 gr_def: i64,
35}
36
37impl<'a> ScopesEncoder<'a> {
38 fn new(names: &'a mut Vec<String>) -> Self {
39 let name_map: HashMap<String, u32> =
40 names.iter().enumerate().map(|(i, n)| (n.clone(), i as u32)).collect();
41
42 Self {
43 output: Vec::with_capacity(256),
44 names,
45 name_map,
46 first_item: true,
47 os_line: 0,
48 os_col: 0,
49 os_name: 0,
50 os_kind: 0,
51 os_var: 0,
52 gr_line: 0,
53 gr_col: 0,
54 gr_def: 0,
55 }
56 }
57
58 #[inline]
59 fn emit_comma(&mut self) {
60 if !self.first_item {
61 self.output.push(b',');
62 }
63 self.first_item = false;
64 }
65
66 #[inline]
67 fn emit_tag(&mut self, tag: u64) {
68 vlq_encode_unsigned(&mut self.output, tag);
69 }
70
71 #[inline]
72 fn emit_unsigned(&mut self, value: u64) {
73 vlq_encode_unsigned(&mut self.output, value);
74 }
75
76 #[inline]
77 fn emit_signed(&mut self, value: i64) {
78 vlq_encode(&mut self.output, value);
79 }
80
81 #[inline]
82 fn name_idx(&mut self, name: &str) -> u32 {
83 resolve_or_add_name(name, self.names, &mut self.name_map)
84 }
85
86 fn encode(mut self, info: &ScopeInfo) -> String {
87 for scope in &info.scopes {
89 match scope {
90 Some(s) => {
91 self.os_line = 0;
93 self.os_col = 0;
94 self.encode_original_scope(s);
95 }
96 None => {
97 self.emit_comma();
99 }
100 }
101 }
102
103 for range in &info.ranges {
105 self.encode_generated_range(range);
106 }
107
108 debug_assert!(self.output.is_ascii());
109 unsafe { String::from_utf8_unchecked(self.output) }
112 }
113
114 fn encode_original_scope(&mut self, scope: &OriginalScope) {
115 self.emit_comma();
117 self.emit_tag(TAG_ORIGINAL_SCOPE_START);
118
119 let mut flags: u64 = 0;
120 if scope.name.is_some() {
121 flags |= crate::OS_FLAG_HAS_NAME;
122 }
123 if scope.kind.is_some() {
124 flags |= crate::OS_FLAG_HAS_KIND;
125 }
126 if scope.is_stack_frame {
127 flags |= crate::OS_FLAG_IS_STACK_FRAME;
128 }
129 self.emit_unsigned(flags);
130
131 let line_delta = scope.start.line - self.os_line;
133 self.emit_unsigned(line_delta as u64);
134 self.os_line = scope.start.line;
135
136 let col =
138 if line_delta != 0 { scope.start.column } else { scope.start.column - self.os_col };
139 self.emit_unsigned(col as u64);
140 self.os_col = scope.start.column;
141
142 if let Some(ref name) = scope.name {
144 let idx = self.name_idx(name) as i64;
145 self.emit_signed(idx - self.os_name);
146 self.os_name = idx;
147 }
148
149 if let Some(ref kind) = scope.kind {
151 let idx = self.name_idx(kind) as i64;
152 self.emit_signed(idx - self.os_kind);
153 self.os_kind = idx;
154 }
155
156 if !scope.variables.is_empty() {
158 self.emit_comma();
159 self.emit_tag(TAG_ORIGINAL_SCOPE_VARIABLES);
160 for var in &scope.variables {
161 let idx = self.name_idx(var) as i64;
162 self.emit_signed(idx - self.os_var);
163 self.os_var = idx;
164 }
165 }
166
167 for child in &scope.children {
169 self.encode_original_scope(child);
170 }
171
172 self.emit_comma();
174 self.emit_tag(TAG_ORIGINAL_SCOPE_END);
175
176 let line_delta = scope.end.line - self.os_line;
177 self.emit_unsigned(line_delta as u64);
178 self.os_line = scope.end.line;
179
180 let col = if line_delta != 0 { scope.end.column } else { scope.end.column - self.os_col };
181 self.emit_unsigned(col as u64);
182 self.os_col = scope.end.column;
183 }
184
185 fn encode_generated_range(&mut self, range: &GeneratedRange) {
186 self.encode_generated_range_start(range);
187 self.encode_generated_range_bindings(range);
188 self.encode_generated_range_sub_range_bindings(range);
189
190 if let Some(ref cs) = range.call_site {
192 self.emit_comma();
193 self.emit_tag(TAG_GENERATED_RANGE_CALL_SITE);
194 self.emit_unsigned(cs.source_index as u64);
195 self.emit_unsigned(cs.line as u64);
196 self.emit_unsigned(cs.column as u64);
197 }
198
199 for child in &range.children {
201 self.encode_generated_range(child);
202 }
203
204 self.emit_comma();
206 self.emit_tag(TAG_GENERATED_RANGE_END);
207
208 let line_delta = range.end.line - self.gr_line;
209 if line_delta != 0 {
210 self.emit_unsigned(line_delta as u64);
211 }
212 self.gr_line = range.end.line;
213
214 let col = if line_delta != 0 { range.end.column } else { range.end.column - self.gr_col };
215 self.emit_unsigned(col as u64);
216 self.gr_col = range.end.column;
217 }
218
219 fn encode_generated_range_start(&mut self, range: &GeneratedRange) {
220 self.emit_comma();
221 self.emit_tag(TAG_GENERATED_RANGE_START);
222
223 let line_delta = range.start.line - self.gr_line;
224
225 let mut flags: u64 = 0;
226 if line_delta != 0 {
227 flags |= crate::GR_FLAG_HAS_LINE;
228 }
229 if range.definition.is_some() {
230 flags |= crate::GR_FLAG_HAS_DEFINITION;
231 }
232 if range.is_stack_frame {
233 flags |= crate::GR_FLAG_IS_STACK_FRAME;
234 }
235 if range.is_hidden {
236 flags |= crate::GR_FLAG_IS_HIDDEN;
237 }
238 self.emit_unsigned(flags);
239
240 if line_delta != 0 {
241 self.emit_unsigned(line_delta as u64);
242 }
243 self.gr_line = range.start.line;
244
245 let col =
246 if line_delta != 0 { range.start.column } else { range.start.column - self.gr_col };
247 self.emit_unsigned(col as u64);
248 self.gr_col = range.start.column;
249
250 if let Some(def) = range.definition {
251 let def_val = def as i64;
252 self.emit_signed(def_val - self.gr_def);
253 self.gr_def = def_val;
254 }
255 }
256
257 fn encode_generated_range_bindings(&mut self, range: &GeneratedRange) {
258 if range.bindings.is_empty() {
259 return;
260 }
261
262 self.emit_comma();
263 self.emit_tag(TAG_GENERATED_RANGE_BINDINGS);
264 for binding in &range.bindings {
265 match binding {
266 Binding::Expression(expr) => {
267 let idx = self.name_idx(expr);
268 self.emit_unsigned(idx as u64 + 1); }
270 Binding::Unavailable => {
271 self.emit_unsigned(0);
272 }
273 Binding::SubRanges(subs) => {
274 if let Some(first) = subs.first() {
276 match &first.expression {
277 Some(expr) => {
278 let idx = self.name_idx(expr);
279 self.emit_unsigned(idx as u64 + 1);
280 }
281 None => {
282 self.emit_unsigned(0);
283 }
284 }
285 } else {
286 self.emit_unsigned(0);
287 }
288 }
289 }
290 }
291 }
292
293 fn encode_generated_range_sub_range_bindings(&mut self, range: &GeneratedRange) {
294 let mut h_var_idx = 0u64;
295 for (i, binding) in range.bindings.iter().enumerate() {
296 let Binding::SubRanges(subs) = binding else {
297 continue;
298 };
299 if subs.len() <= 1 {
300 continue;
301 }
302
303 self.emit_comma();
304 self.emit_tag(TAG_GENERATED_RANGE_SUB_RANGE_BINDINGS);
305
306 let var_delta = i as u64 - h_var_idx;
308 self.emit_unsigned(var_delta);
309 h_var_idx = i as u64;
310
311 let mut h_line = range.start.line;
313 let mut h_col = range.start.column;
314
315 for sub in &subs[1..] {
317 match &sub.expression {
319 Some(expr) => {
320 let idx = self.name_idx(expr);
321 self.emit_unsigned(idx as u64 + 1);
322 }
323 None => {
324 self.emit_unsigned(0);
325 }
326 }
327
328 let sub_line_delta = sub.from.line - h_line;
329 self.emit_unsigned(sub_line_delta as u64);
330 h_line = sub.from.line;
331
332 let sub_col =
333 if sub_line_delta != 0 { sub.from.column } else { sub.from.column - h_col };
334 self.emit_unsigned(sub_col as u64);
335 h_col = sub.from.column;
336 }
337 }
338 }
339}
340
341pub fn encode_scopes(info: &ScopeInfo, names: &mut Vec<String>) -> String {
345 let encoder = ScopesEncoder::new(names);
346 encoder.encode(info)
347}