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