1#![allow(clippy::cast_possible_truncation)]
19
20use log::warn;
21
22pub const TAG_TERM_NAME: u8 = 1;
28pub const TAG_TYPE_NAME: u8 = 2;
30pub const TAG_NONE_SYM: u8 = 3;
32pub const TAG_TYPE_SYM: u8 = 4;
34pub const TAG_ALIAS_SYM: u8 = 5;
36pub const TAG_CLASS_SYM: u8 = 6;
38pub const TAG_MODULE_SYM: u8 = 7;
40pub const TAG_VAL_SYM: u8 = 8;
42pub const TAG_EXT_REF: u8 = 9;
44pub const TAG_EXT_MOD_CLASS_REF: u8 = 10;
46
47pub const FLAG_PRIVATE: u64 = 1 << 2;
53pub const FLAG_PROTECTED: u64 = 1 << 3;
55pub const FLAG_SEALED: u64 = 1 << 5;
57pub const FLAG_CASE: u64 = 1 << 7;
59pub const FLAG_ABSTRACT: u64 = 1 << 8;
61pub const FLAG_MODULE: u64 = 1 << 11;
63pub const FLAG_INTERFACE: u64 = 1 << 13;
65pub const FLAG_TRAIT: u64 = 1 << 36;
67
68#[derive(Debug, Clone)]
74pub struct SignatureEntry {
75 pub tag: u8,
77 pub data: Vec<u8>,
79}
80
81#[derive(Debug, Clone)]
87pub struct ScalaSymbolInfo {
88 pub name_index: usize,
90 pub owner_index: usize,
92 pub flags: u64,
94 pub info_index: usize,
96}
97
98#[derive(Debug)]
107pub struct ScalaSignatureReader {
108 entries: Vec<SignatureEntry>,
110}
111
112impl ScalaSignatureReader {
113 #[must_use]
118 pub fn parse(bytes: &[u8]) -> Option<Self> {
119 if bytes.len() < 2 {
121 warn!("scala signature too short ({} bytes)", bytes.len());
122 return None;
123 }
124
125 let major = bytes[0];
126 let minor = bytes[1];
127
128 if major != 5 {
130 warn!("unsupported Scala signature version {major}.{minor} (expected 5.x)");
131 return None;
132 }
133
134 let mut pos = 2;
135
136 let entry_count = read_nat(bytes, &mut pos)? as usize;
138
139 let mut entries = Vec::with_capacity(entry_count);
140 for _ in 0..entry_count {
141 let entry = read_entry(bytes, &mut pos)?;
142 entries.push(entry);
143 }
144
145 Some(Self { entries })
146 }
147
148 #[must_use]
150 pub fn entry_count(&self) -> usize {
151 self.entries.len()
152 }
153
154 #[must_use]
156 pub fn entry(&self, index: usize) -> Option<&SignatureEntry> {
157 self.entries.get(index)
158 }
159
160 #[must_use]
164 pub fn read_name(&self, index: usize) -> Option<String> {
165 let entry = self.entry(index)?;
166 if entry.tag != TAG_TERM_NAME && entry.tag != TAG_TYPE_NAME {
167 return None;
168 }
169 String::from_utf8(entry.data.clone()).ok()
170 }
171
172 #[must_use]
181 pub fn read_symbol_info(&self, entry: &SignatureEntry) -> Option<ScalaSymbolInfo> {
182 if entry.tag != TAG_CLASS_SYM && entry.tag != TAG_MODULE_SYM {
183 return None;
184 }
185 parse_symbol_info(&entry.data)
186 }
187
188 #[must_use]
192 #[allow(clippy::items_after_statements)] #[allow(clippy::match_same_arms)] #[allow(clippy::manual_let_else)] pub fn resolve_qualified_name(&self, sym_index: usize) -> Option<String> {
196 let entry = self.entry(sym_index)?;
197 let info = self.read_symbol_info(entry)?;
198 let name = self.read_name(info.name_index)?;
199
200 let mut segments = vec![name];
202 let mut current_owner = info.owner_index;
203
204 const MAX_DEPTH: usize = 128;
206 for _ in 0..MAX_DEPTH {
207 let owner_entry = match self.entry(current_owner) {
208 Some(e) => e,
209 None => break,
210 };
211
212 match owner_entry.tag {
213 TAG_CLASS_SYM | TAG_MODULE_SYM => {
214 if let Some(owner_info) = self.read_symbol_info(owner_entry) {
215 if let Some(owner_name) = self.read_name(owner_info.name_index) {
216 segments.push(owner_name);
217 current_owner = owner_info.owner_index;
218 } else {
219 break;
220 }
221 } else {
222 break;
223 }
224 }
225 TAG_EXT_REF | TAG_EXT_MOD_CLASS_REF => {
226 if let Some(ext_name) = self.read_ext_ref_name(owner_entry) {
227 if ext_name != "<empty>" {
229 segments.push(ext_name);
230 }
231 }
232 break;
233 }
234 TAG_NONE_SYM => break,
235 _ => break,
236 }
237 }
238
239 segments.reverse();
240 Some(segments.join("."))
241 }
242
243 #[must_use]
247 fn read_ext_ref_name(&self, entry: &SignatureEntry) -> Option<String> {
248 if entry.tag != TAG_EXT_REF && entry.tag != TAG_EXT_MOD_CLASS_REF {
249 return None;
250 }
251 let mut pos = 0;
252 let name_index = read_nat(&entry.data, &mut pos)? as usize;
253 self.read_name(name_index)
254 }
255
256 #[must_use]
261 pub fn read_ext_ref_owner(&self, entry: &SignatureEntry) -> Option<usize> {
262 if entry.tag != TAG_EXT_REF && entry.tag != TAG_EXT_MOD_CLASS_REF {
263 return None;
264 }
265 let mut pos = 0;
266 let _name_index = read_nat(&entry.data, &mut pos)?;
267 if pos < entry.data.len() {
269 Some(read_nat(&entry.data, &mut pos)? as usize)
270 } else {
271 None
272 }
273 }
274
275 #[must_use]
277 pub fn class_and_module_symbols(&self) -> Vec<(usize, &SignatureEntry)> {
278 self.entries
279 .iter()
280 .enumerate()
281 .filter(|(_, e)| e.tag == TAG_CLASS_SYM || e.tag == TAG_MODULE_SYM)
282 .collect()
283 }
284
285 #[must_use]
287 pub fn ext_refs(&self) -> Vec<(usize, &SignatureEntry)> {
288 self.entries
289 .iter()
290 .enumerate()
291 .filter(|(_, e)| e.tag == TAG_EXT_REF || e.tag == TAG_EXT_MOD_CLASS_REF)
292 .collect()
293 }
294}
295
296pub fn read_nat(data: &[u8], pos: &mut usize) -> Option<u64> {
305 let mut result: u64 = 0;
306 let mut shift: u32 = 0;
307
308 loop {
309 if *pos >= data.len() {
310 return None;
311 }
312 let byte = data[*pos];
313 *pos += 1;
314
315 let value = u64::from(byte & 0x7F);
317
318 result = result.checked_add(value.checked_shl(shift)?)?;
320 shift += 7;
321
322 if byte & 0x80 == 0 {
324 return Some(result);
325 }
326
327 if shift > 63 {
329 return None;
330 }
331 }
332}
333
334pub fn read_long_nat(data: &[u8], pos: &mut usize) -> Option<u64> {
339 read_nat(data, pos)
340}
341
342fn read_entry(data: &[u8], pos: &mut usize) -> Option<SignatureEntry> {
348 if *pos >= data.len() {
349 return None;
350 }
351 let tag = data[*pos];
352 *pos += 1;
353
354 let length = read_nat(data, pos)? as usize;
355
356 if *pos + length > data.len() {
358 return None;
359 }
360
361 let entry_data = data[*pos..*pos + length].to_vec();
362 *pos += length;
363
364 Some(SignatureEntry {
365 tag,
366 data: entry_data,
367 })
368}
369
370fn parse_symbol_info(data: &[u8]) -> Option<ScalaSymbolInfo> {
378 let mut pos = 0;
379 let name_index = read_nat(data, &mut pos)? as usize;
380 let owner_index = read_nat(data, &mut pos)? as usize;
381 let flags = read_long_nat(data, &mut pos)?;
382
383 let mut remaining_nats = Vec::new();
389 while pos < data.len() {
390 match read_nat(data, &mut pos) {
391 Some(v) => remaining_nats.push(v as usize),
392 None => break,
393 }
394 }
395
396 let info_index = remaining_nats.pop().unwrap_or(0);
398
399 Some(ScalaSymbolInfo {
400 name_index,
401 owner_index,
402 flags,
403 info_index,
404 })
405}
406
407#[cfg(test)]
412mod tests {
413 use super::*;
414
415 fn encode_nat(mut value: u64) -> Vec<u8> {
419 let mut bytes = Vec::new();
420 loop {
421 let mut byte = (value & 0x7F) as u8;
422 value >>= 7;
423 if value != 0 {
424 byte |= 0x80;
425 }
426 bytes.push(byte);
427 if value == 0 {
428 break;
429 }
430 }
431 bytes
432 }
433
434 fn build_entry(tag: u8, data: &[u8]) -> Vec<u8> {
436 let mut entry = vec![tag];
437 entry.extend(encode_nat(data.len() as u64));
438 entry.extend_from_slice(data);
439 entry
440 }
441
442 fn build_signature(entries: Vec<Vec<u8>>) -> Vec<u8> {
444 let mut buf = vec![5, 0]; buf.extend(encode_nat(entries.len() as u64));
446 for entry in entries {
447 buf.extend(entry);
448 }
449 buf
450 }
451
452 #[test]
455 fn nat_single_byte() {
456 let data = [42];
457 let mut pos = 0;
458 assert_eq!(read_nat(&data, &mut pos), Some(42));
459 assert_eq!(pos, 1);
460 }
461
462 #[test]
463 fn nat_zero() {
464 let data = [0];
465 let mut pos = 0;
466 assert_eq!(read_nat(&data, &mut pos), Some(0));
467 assert_eq!(pos, 1);
468 }
469
470 #[test]
471 fn nat_max_single_byte() {
472 let data = [127];
473 let mut pos = 0;
474 assert_eq!(read_nat(&data, &mut pos), Some(127));
475 assert_eq!(pos, 1);
476 }
477
478 #[test]
479 fn nat_two_bytes() {
480 let data = [0x80, 0x01];
484 let mut pos = 0;
485 assert_eq!(read_nat(&data, &mut pos), Some(128));
486 assert_eq!(pos, 2);
487 }
488
489 #[test]
490 fn nat_multi_byte_300() {
491 let data = [0xAC, 0x02];
494 let mut pos = 0;
495 assert_eq!(read_nat(&data, &mut pos), Some(300));
496 assert_eq!(pos, 2);
497 }
498
499 #[test]
500 fn nat_round_trip() {
501 for value in [0, 1, 127, 128, 255, 300, 16383, 16384, 65535, 1_000_000] {
502 let encoded = encode_nat(value);
503 let mut pos = 0;
504 assert_eq!(
505 read_nat(&encoded, &mut pos),
506 Some(value),
507 "round-trip failed for {value}"
508 );
509 assert_eq!(pos, encoded.len());
510 }
511 }
512
513 #[test]
514 fn nat_truncated_returns_none() {
515 let data = [0x80];
517 let mut pos = 0;
518 assert_eq!(read_nat(&data, &mut pos), None);
519 }
520
521 #[test]
522 fn nat_empty_returns_none() {
523 let data: [u8; 0] = [];
524 let mut pos = 0;
525 assert_eq!(read_nat(&data, &mut pos), None);
526 }
527
528 #[test]
531 fn parse_empty_signature() {
532 let sig = build_signature(vec![]);
533 let reader = ScalaSignatureReader::parse(&sig).unwrap();
534 assert_eq!(reader.entry_count(), 0);
535 }
536
537 #[test]
538 fn parse_name_entries() {
539 let name_data = b"MyClass".to_vec();
540 let name_entry = build_entry(TAG_TYPE_NAME, &name_data);
541
542 let sig = build_signature(vec![name_entry]);
543 let reader = ScalaSignatureReader::parse(&sig).unwrap();
544
545 assert_eq!(reader.entry_count(), 1);
546 assert_eq!(reader.read_name(0), Some("MyClass".to_string()));
547 }
548
549 #[test]
550 fn parse_term_name() {
551 let name_data = b"myVal".to_vec();
552 let name_entry = build_entry(TAG_TERM_NAME, &name_data);
553
554 let sig = build_signature(vec![name_entry]);
555 let reader = ScalaSignatureReader::parse(&sig).unwrap();
556
557 assert_eq!(reader.read_name(0), Some("myVal".to_string()));
558 }
559
560 #[test]
561 fn read_name_wrong_tag_returns_none() {
562 let entry = build_entry(TAG_CLASS_SYM, b"data");
563 let sig = build_signature(vec![entry]);
564 let reader = ScalaSignatureReader::parse(&sig).unwrap();
565
566 assert_eq!(reader.read_name(0), None);
567 }
568
569 #[test]
570 fn read_name_out_of_bounds_returns_none() {
571 let sig = build_signature(vec![]);
572 let reader = ScalaSignatureReader::parse(&sig).unwrap();
573 assert_eq!(reader.read_name(0), None);
574 }
575
576 #[test]
577 fn parse_class_sym_entry() {
578 let name = build_entry(TAG_TYPE_NAME, b"Point");
580 let owner = build_entry(TAG_NONE_SYM, &[]);
581
582 let mut sym_data = Vec::new();
584 sym_data.extend(encode_nat(0)); sym_data.extend(encode_nat(1)); sym_data.extend(encode_nat(FLAG_CASE)); sym_data.extend(encode_nat(0)); let class_sym = build_entry(TAG_CLASS_SYM, &sym_data);
589
590 let sig = build_signature(vec![name, owner, class_sym]);
591 let reader = ScalaSignatureReader::parse(&sig).unwrap();
592
593 let entry = reader.entry(2).unwrap();
594 assert_eq!(entry.tag, TAG_CLASS_SYM);
595
596 let info = reader.read_symbol_info(entry).unwrap();
597 assert_eq!(info.name_index, 0);
598 assert_eq!(info.owner_index, 1);
599 assert_eq!(info.flags & FLAG_CASE, FLAG_CASE);
600 assert_eq!(reader.read_name(info.name_index), Some("Point".to_string()));
601 }
602
603 #[test]
604 fn parse_module_sym_entry() {
605 let name = build_entry(TAG_TERM_NAME, b"Config");
606 let owner = build_entry(TAG_NONE_SYM, &[]);
607
608 let mut sym_data = Vec::new();
609 sym_data.extend(encode_nat(0)); sym_data.extend(encode_nat(1)); sym_data.extend(encode_nat(FLAG_MODULE)); sym_data.extend(encode_nat(0)); let mod_sym = build_entry(TAG_MODULE_SYM, &sym_data);
614
615 let sig = build_signature(vec![name, owner, mod_sym]);
616 let reader = ScalaSignatureReader::parse(&sig).unwrap();
617
618 let entry = reader.entry(2).unwrap();
619 assert_eq!(entry.tag, TAG_MODULE_SYM);
620
621 let info = reader.read_symbol_info(entry).unwrap();
622 assert_eq!(info.flags & FLAG_MODULE, FLAG_MODULE);
623 }
624
625 #[test]
626 fn class_and_module_symbols_finds_all() {
627 let name1 = build_entry(TAG_TYPE_NAME, b"A");
628 let name2 = build_entry(TAG_TERM_NAME, b"B");
629 let owner = build_entry(TAG_NONE_SYM, &[]);
630
631 let mut cls_data = Vec::new();
632 cls_data.extend(encode_nat(0));
633 cls_data.extend(encode_nat(2));
634 cls_data.extend(encode_nat(0));
635 cls_data.extend(encode_nat(0));
636 let cls = build_entry(TAG_CLASS_SYM, &cls_data);
637
638 let mut mod_data = Vec::new();
639 mod_data.extend(encode_nat(1));
640 mod_data.extend(encode_nat(2));
641 mod_data.extend(encode_nat(0));
642 mod_data.extend(encode_nat(0));
643 let module = build_entry(TAG_MODULE_SYM, &mod_data);
644
645 let sig = build_signature(vec![name1, name2, owner, cls, module]);
646 let reader = ScalaSignatureReader::parse(&sig).unwrap();
647
648 let symbols = reader.class_and_module_symbols();
649 assert_eq!(symbols.len(), 2);
650 assert_eq!(symbols[0].0, 3); assert_eq!(symbols[1].0, 4); }
653
654 #[test]
655 fn ext_ref_name_resolution() {
656 let name = build_entry(TAG_TERM_NAME, b"scala");
657 let mut ext_data = Vec::new();
658 ext_data.extend(encode_nat(0)); let ext = build_entry(TAG_EXT_REF, &ext_data);
660
661 let sig = build_signature(vec![name, ext]);
662 let reader = ScalaSignatureReader::parse(&sig).unwrap();
663
664 let ext_entry = reader.entry(1).unwrap();
665 assert_eq!(
666 reader.read_ext_ref_name(ext_entry),
667 Some("scala".to_string())
668 );
669 }
670
671 #[test]
672 fn ext_ref_with_owner() {
673 let name = build_entry(TAG_TERM_NAME, b"Option");
674 let owner_name = build_entry(TAG_TERM_NAME, b"scala");
675 let mut owner_ext_data = Vec::new();
676 owner_ext_data.extend(encode_nat(1)); let owner_ext = build_entry(TAG_EXT_MOD_CLASS_REF, &owner_ext_data);
678
679 let mut ext_data = Vec::new();
680 ext_data.extend(encode_nat(0)); ext_data.extend(encode_nat(2)); let ext = build_entry(TAG_EXT_REF, &ext_data);
683
684 let sig = build_signature(vec![name, owner_name, owner_ext, ext]);
685 let reader = ScalaSignatureReader::parse(&sig).unwrap();
686
687 let entry = reader.entry(3).unwrap();
688 assert_eq!(reader.read_ext_ref_owner(entry), Some(2));
689 }
690
691 #[test]
694 fn unsupported_major_version_returns_none() {
695 let mut sig = build_signature(vec![]);
696 sig[0] = 4; assert!(ScalaSignatureReader::parse(&sig).is_none());
698 }
699
700 #[test]
701 fn too_short_returns_none() {
702 assert!(ScalaSignatureReader::parse(&[5]).is_none());
703 assert!(ScalaSignatureReader::parse(&[]).is_none());
704 }
705
706 #[test]
709 fn truncated_entry_returns_none() {
710 let mut data = vec![5, 0]; data.extend(encode_nat(1)); data.push(TAG_TYPE_NAME); data.extend(encode_nat(100)); assert!(ScalaSignatureReader::parse(&data).is_none());
716 }
717
718 #[test]
719 fn symbol_info_from_non_symbol_returns_none() {
720 let name = build_entry(TAG_TYPE_NAME, b"Foo");
721 let sig = build_signature(vec![name]);
722 let reader = ScalaSignatureReader::parse(&sig).unwrap();
723
724 let entry = reader.entry(0).unwrap();
725 assert!(reader.read_symbol_info(entry).is_none());
726 }
727
728 #[test]
731 fn trait_flag_detection() {
732 let name = build_entry(TAG_TYPE_NAME, b"Functor");
733 let owner = build_entry(TAG_NONE_SYM, &[]);
734
735 let flags = FLAG_TRAIT | FLAG_INTERFACE | FLAG_ABSTRACT;
737 let mut sym_data = Vec::new();
738 sym_data.extend(encode_nat(0)); sym_data.extend(encode_nat(1)); sym_data.extend(encode_nat(flags)); sym_data.extend(encode_nat(0)); let cls = build_entry(TAG_CLASS_SYM, &sym_data);
743
744 let sig = build_signature(vec![name, owner, cls]);
745 let reader = ScalaSignatureReader::parse(&sig).unwrap();
746
747 let entry = reader.entry(2).unwrap();
748 let info = reader.read_symbol_info(entry).unwrap();
749 assert_ne!(info.flags & FLAG_TRAIT, 0);
750 assert_ne!(info.flags & FLAG_INTERFACE, 0);
751 assert_ne!(info.flags & FLAG_ABSTRACT, 0);
752 }
753
754 #[test]
755 fn sealed_flag_detection() {
756 let name = build_entry(TAG_TYPE_NAME, b"Expr");
757 let owner = build_entry(TAG_NONE_SYM, &[]);
758
759 let flags = FLAG_SEALED | FLAG_ABSTRACT | FLAG_TRAIT | FLAG_INTERFACE;
760 let mut sym_data = Vec::new();
761 sym_data.extend(encode_nat(0));
762 sym_data.extend(encode_nat(1));
763 sym_data.extend(encode_nat(flags));
764 sym_data.extend(encode_nat(0));
765 let cls = build_entry(TAG_CLASS_SYM, &sym_data);
766
767 let sig = build_signature(vec![name, owner, cls]);
768 let reader = ScalaSignatureReader::parse(&sig).unwrap();
769
770 let entry = reader.entry(2).unwrap();
771 let info = reader.read_symbol_info(entry).unwrap();
772 assert_ne!(info.flags & FLAG_SEALED, 0);
773 }
774
775 #[test]
776 fn private_and_protected_flags() {
777 let name = build_entry(TAG_TYPE_NAME, b"Inner");
779 let owner = build_entry(TAG_NONE_SYM, &[]);
780
781 let mut sym_data = Vec::new();
782 sym_data.extend(encode_nat(0));
783 sym_data.extend(encode_nat(1));
784 sym_data.extend(encode_nat(FLAG_PRIVATE));
785 sym_data.extend(encode_nat(0));
786 let cls = build_entry(TAG_CLASS_SYM, &sym_data);
787
788 let sig = build_signature(vec![name, owner, cls]);
789 let reader = ScalaSignatureReader::parse(&sig).unwrap();
790 let entry = reader.entry(2).unwrap();
791 let info = reader.read_symbol_info(entry).unwrap();
792 assert_ne!(info.flags & FLAG_PRIVATE, 0);
793 assert_eq!(info.flags & FLAG_PROTECTED, 0);
794 }
795
796 #[test]
797 fn large_nat_flag_value() {
798 let encoded = encode_nat(FLAG_TRAIT);
800 let mut pos = 0;
801 let decoded = read_nat(&encoded, &mut pos).unwrap();
802 assert_eq!(decoded, FLAG_TRAIT);
803 assert_eq!(decoded, 1 << 36);
804 }
805}