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> = names
40 .iter()
41 .enumerate()
42 .map(|(i, n)| (n.clone(), i as u32))
43 .collect();
44
45 Self {
46 output: Vec::with_capacity(256),
47 names,
48 name_map,
49 first_item: true,
50 os_line: 0,
51 os_col: 0,
52 os_name: 0,
53 os_kind: 0,
54 os_var: 0,
55 gr_line: 0,
56 gr_col: 0,
57 gr_def: 0,
58 }
59 }
60
61 #[inline]
62 fn emit_comma(&mut self) {
63 if !self.first_item {
64 self.output.push(b',');
65 }
66 self.first_item = false;
67 }
68
69 #[inline]
70 fn emit_tag(&mut self, tag: u64) {
71 vlq_encode_unsigned(&mut self.output, tag);
72 }
73
74 #[inline]
75 fn emit_unsigned(&mut self, value: u64) {
76 vlq_encode_unsigned(&mut self.output, value);
77 }
78
79 #[inline]
80 fn emit_signed(&mut self, value: i64) {
81 vlq_encode(&mut self.output, value);
82 }
83
84 #[inline]
85 fn name_idx(&mut self, name: &str) -> u32 {
86 resolve_or_add_name(name, self.names, &mut self.name_map)
87 }
88
89 fn encode(mut self, info: &ScopeInfo) -> String {
90 for scope in &info.scopes {
92 match scope {
93 Some(s) => {
94 self.os_line = 0;
96 self.os_col = 0;
97 self.encode_original_scope(s);
98 }
99 None => {
100 self.emit_comma();
102 }
103 }
104 }
105
106 for range in &info.ranges {
108 self.encode_generated_range(range);
109 }
110
111 debug_assert!(self.output.is_ascii());
114 unsafe { String::from_utf8_unchecked(self.output) }
115 }
116
117 fn encode_original_scope(&mut self, scope: &OriginalScope) {
118 self.emit_comma();
120 self.emit_tag(TAG_ORIGINAL_SCOPE_START);
121
122 let mut flags: u64 = 0;
123 if scope.name.is_some() {
124 flags |= crate::OS_FLAG_HAS_NAME;
125 }
126 if scope.kind.is_some() {
127 flags |= crate::OS_FLAG_HAS_KIND;
128 }
129 if scope.is_stack_frame {
130 flags |= crate::OS_FLAG_IS_STACK_FRAME;
131 }
132 self.emit_unsigned(flags);
133
134 let line_delta = scope.start.line - self.os_line;
136 self.emit_unsigned(line_delta as u64);
137 self.os_line = scope.start.line;
138
139 let col = if line_delta != 0 {
141 scope.start.column
142 } else {
143 scope.start.column - self.os_col
144 };
145 self.emit_unsigned(col as u64);
146 self.os_col = scope.start.column;
147
148 if let Some(ref name) = scope.name {
150 let idx = self.name_idx(name) as i64;
151 self.emit_signed(idx - self.os_name);
152 self.os_name = idx;
153 }
154
155 if let Some(ref kind) = scope.kind {
157 let idx = self.name_idx(kind) as i64;
158 self.emit_signed(idx - self.os_kind);
159 self.os_kind = idx;
160 }
161
162 if !scope.variables.is_empty() {
164 self.emit_comma();
165 self.emit_tag(TAG_ORIGINAL_SCOPE_VARIABLES);
166 for var in &scope.variables {
167 let idx = self.name_idx(var) as i64;
168 self.emit_signed(idx - self.os_var);
169 self.os_var = idx;
170 }
171 }
172
173 for child in &scope.children {
175 self.encode_original_scope(child);
176 }
177
178 self.emit_comma();
180 self.emit_tag(TAG_ORIGINAL_SCOPE_END);
181
182 let line_delta = scope.end.line - self.os_line;
183 self.emit_unsigned(line_delta as u64);
184 self.os_line = scope.end.line;
185
186 let col = if line_delta != 0 {
187 scope.end.column
188 } else {
189 scope.end.column - self.os_col
190 };
191 self.emit_unsigned(col as u64);
192 self.os_col = scope.end.column;
193 }
194
195 fn encode_generated_range(&mut self, range: &GeneratedRange) {
196 self.emit_comma();
198 self.emit_tag(TAG_GENERATED_RANGE_START);
199
200 let line_delta = range.start.line - self.gr_line;
201
202 let mut flags: u64 = 0;
203 if line_delta != 0 {
204 flags |= crate::GR_FLAG_HAS_LINE;
205 }
206 if range.definition.is_some() {
207 flags |= crate::GR_FLAG_HAS_DEFINITION;
208 }
209 if range.is_stack_frame {
210 flags |= crate::GR_FLAG_IS_STACK_FRAME;
211 }
212 if range.is_hidden {
213 flags |= crate::GR_FLAG_IS_HIDDEN;
214 }
215 self.emit_unsigned(flags);
216
217 if line_delta != 0 {
218 self.emit_unsigned(line_delta as u64);
219 }
220 self.gr_line = range.start.line;
221
222 let col = if line_delta != 0 {
223 range.start.column
224 } else {
225 range.start.column - self.gr_col
226 };
227 self.emit_unsigned(col as u64);
228 self.gr_col = range.start.column;
229
230 if let Some(def) = range.definition {
231 let def_val = def as i64;
232 self.emit_signed(def_val - self.gr_def);
233 self.gr_def = def_val;
234 }
235
236 if !range.bindings.is_empty() {
238 self.emit_comma();
239 self.emit_tag(TAG_GENERATED_RANGE_BINDINGS);
240 for binding in &range.bindings {
241 match binding {
242 Binding::Expression(expr) => {
243 let idx = self.name_idx(expr);
244 self.emit_unsigned(idx as u64 + 1); }
246 Binding::Unavailable => {
247 self.emit_unsigned(0);
248 }
249 Binding::SubRanges(subs) => {
250 if let Some(first) = subs.first() {
252 match &first.expression {
253 Some(expr) => {
254 let idx = self.name_idx(expr);
255 self.emit_unsigned(idx as u64 + 1);
256 }
257 None => {
258 self.emit_unsigned(0);
259 }
260 }
261 } else {
262 self.emit_unsigned(0);
263 }
264 }
265 }
266 }
267 }
268
269 let mut h_var_idx = 0u64;
271 let mut h_first = true;
272 for (i, binding) in range.bindings.iter().enumerate() {
273 if let Binding::SubRanges(subs) = binding
274 && subs.len() > 1
275 {
276 self.emit_comma();
277 self.emit_tag(TAG_GENERATED_RANGE_SUB_RANGE_BINDINGS);
278
279 let var_delta = i as u64 - if h_first { 0 } else { h_var_idx };
281 self.emit_unsigned(var_delta);
282 h_var_idx = i as u64;
283 h_first = false;
284
285 let mut h_line = range.start.line;
287 let mut h_col = range.start.column;
288
289 for sub in &subs[1..] {
291 match &sub.expression {
293 Some(expr) => {
294 let idx = self.name_idx(expr);
295 self.emit_unsigned(idx as u64 + 1);
296 }
297 None => {
298 self.emit_unsigned(0);
299 }
300 }
301
302 let sub_line_delta = sub.from.line - h_line;
303 self.emit_unsigned(sub_line_delta as u64);
304 h_line = sub.from.line;
305
306 let sub_col = if sub_line_delta != 0 {
307 sub.from.column
308 } else {
309 sub.from.column - h_col
310 };
311 self.emit_unsigned(sub_col as u64);
312 h_col = sub.from.column;
313 }
314 }
315 }
316
317 if let Some(ref cs) = range.call_site {
319 self.emit_comma();
320 self.emit_tag(TAG_GENERATED_RANGE_CALL_SITE);
321 self.emit_unsigned(cs.source_index as u64);
322 self.emit_unsigned(cs.line as u64);
323 self.emit_unsigned(cs.column as u64);
324 }
325
326 for child in &range.children {
328 self.encode_generated_range(child);
329 }
330
331 self.emit_comma();
333 self.emit_tag(TAG_GENERATED_RANGE_END);
334
335 let line_delta = range.end.line - self.gr_line;
336 if line_delta != 0 {
337 self.emit_unsigned(line_delta as u64);
338 }
339 self.gr_line = range.end.line;
340
341 let col = if line_delta != 0 {
342 range.end.column
343 } else {
344 range.end.column - self.gr_col
345 };
346 self.emit_unsigned(col as u64);
347 self.gr_col = range.end.column;
348 }
349}
350
351pub fn encode_scopes(info: &ScopeInfo, names: &mut Vec<String>) -> String {
355 let encoder = ScopesEncoder::new(names);
356 encoder.encode(info)
357}