1use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ClassStub {
26 pub fqn: String,
28 pub name: String,
30 pub kind: ClassKind,
32 pub access: AccessFlags,
34 pub superclass: Option<String>,
36 pub interfaces: Vec<String>,
38 pub methods: Vec<MethodStub>,
40 pub fields: Vec<FieldStub>,
42 pub annotations: Vec<AnnotationStub>,
44 pub generic_signature: Option<GenericClassSignature>,
47 pub inner_classes: Vec<InnerClassEntry>,
49 pub lambda_targets: Vec<LambdaTargetStub>,
51 pub module: Option<ModuleStub>,
53 pub record_components: Vec<RecordComponent>,
55 pub enum_constants: Vec<String>,
57 pub source_file: Option<String>,
59 #[serde(default)]
65 pub source_jar: Option<String>,
66 pub kotlin_metadata: Option<KotlinMetadataStub>,
68 pub scala_signature: Option<ScalaSignatureStub>,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
74pub enum ClassKind {
75 Class,
77 Interface,
79 Enum,
81 Annotation,
83 Record,
85 Module,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
98pub struct AccessFlags {
99 bits: u16,
100}
101
102impl AccessFlags {
103 pub const ACC_PUBLIC: u16 = 0x0001;
107 pub const ACC_PRIVATE: u16 = 0x0002;
109 pub const ACC_PROTECTED: u16 = 0x0004;
111 pub const ACC_STATIC: u16 = 0x0008;
113 pub const ACC_FINAL: u16 = 0x0010;
115 pub const ACC_SYNCHRONIZED: u16 = 0x0020;
117 pub const ACC_SUPER: u16 = 0x0020;
119 pub const ACC_VOLATILE: u16 = 0x0040;
121 pub const ACC_BRIDGE: u16 = 0x0040;
123 pub const ACC_TRANSIENT: u16 = 0x0080;
125 pub const ACC_VARARGS: u16 = 0x0080;
127 pub const ACC_NATIVE: u16 = 0x0100;
129 pub const ACC_INTERFACE: u16 = 0x0200;
131 pub const ACC_ABSTRACT: u16 = 0x0400;
133 pub const ACC_STRICT: u16 = 0x0800;
135 pub const ACC_SYNTHETIC: u16 = 0x1000;
137 pub const ACC_ANNOTATION: u16 = 0x2000;
139 pub const ACC_ENUM: u16 = 0x4000;
141 pub const ACC_MODULE: u16 = 0x8000;
143
144 #[must_use]
148 pub const fn new(bits: u16) -> Self {
149 Self { bits }
150 }
151
152 #[must_use]
154 pub const fn empty() -> Self {
155 Self { bits: 0 }
156 }
157
158 #[must_use]
161 pub const fn is_public(&self) -> bool {
162 self.bits & Self::ACC_PUBLIC != 0
163 }
164
165 #[must_use]
166 pub const fn is_private(&self) -> bool {
167 self.bits & Self::ACC_PRIVATE != 0
168 }
169
170 #[must_use]
171 pub const fn is_protected(&self) -> bool {
172 self.bits & Self::ACC_PROTECTED != 0
173 }
174
175 #[must_use]
176 pub const fn is_static(&self) -> bool {
177 self.bits & Self::ACC_STATIC != 0
178 }
179
180 #[must_use]
181 pub const fn is_final(&self) -> bool {
182 self.bits & Self::ACC_FINAL != 0
183 }
184
185 #[must_use]
186 pub const fn is_synchronized(&self) -> bool {
187 self.bits & Self::ACC_SYNCHRONIZED != 0
188 }
189
190 #[must_use]
191 pub const fn is_volatile(&self) -> bool {
192 self.bits & Self::ACC_VOLATILE != 0
193 }
194
195 #[must_use]
196 pub const fn is_bridge(&self) -> bool {
197 self.bits & Self::ACC_BRIDGE != 0
198 }
199
200 #[must_use]
201 pub const fn is_transient(&self) -> bool {
202 self.bits & Self::ACC_TRANSIENT != 0
203 }
204
205 #[must_use]
206 pub const fn is_varargs(&self) -> bool {
207 self.bits & Self::ACC_VARARGS != 0
208 }
209
210 #[must_use]
211 pub const fn is_native(&self) -> bool {
212 self.bits & Self::ACC_NATIVE != 0
213 }
214
215 #[must_use]
216 pub const fn is_interface(&self) -> bool {
217 self.bits & Self::ACC_INTERFACE != 0
218 }
219
220 #[must_use]
221 pub const fn is_abstract(&self) -> bool {
222 self.bits & Self::ACC_ABSTRACT != 0
223 }
224
225 #[must_use]
226 pub const fn is_strict(&self) -> bool {
227 self.bits & Self::ACC_STRICT != 0
228 }
229
230 #[must_use]
231 pub const fn is_synthetic(&self) -> bool {
232 self.bits & Self::ACC_SYNTHETIC != 0
233 }
234
235 #[must_use]
236 pub const fn is_annotation(&self) -> bool {
237 self.bits & Self::ACC_ANNOTATION != 0
238 }
239
240 #[must_use]
241 pub const fn is_enum(&self) -> bool {
242 self.bits & Self::ACC_ENUM != 0
243 }
244
245 #[must_use]
246 pub const fn is_module(&self) -> bool {
247 self.bits & Self::ACC_MODULE != 0
248 }
249
250 #[must_use]
252 pub const fn bits(&self) -> u16 {
253 self.bits
254 }
255
256 #[must_use]
258 pub const fn contains(&self, flag: u16) -> bool {
259 self.bits & flag == flag
260 }
261
262 #[must_use]
264 pub const fn union(self, other: Self) -> Self {
265 Self {
266 bits: self.bits | other.bits,
267 }
268 }
269}
270
271impl std::fmt::Display for AccessFlags {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 let mut parts = Vec::new();
274 if self.is_public() {
275 parts.push("public");
276 }
277 if self.is_private() {
278 parts.push("private");
279 }
280 if self.is_protected() {
281 parts.push("protected");
282 }
283 if self.is_static() {
284 parts.push("static");
285 }
286 if self.is_final() {
287 parts.push("final");
288 }
289 if self.is_abstract() {
290 parts.push("abstract");
291 }
292 if self.is_native() {
293 parts.push("native");
294 }
295 if self.is_synchronized() {
296 parts.push("synchronized");
297 }
298 if self.is_synthetic() {
299 parts.push("synthetic");
300 }
301 write!(f, "{}", parts.join(" "))
302 }
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct MethodStub {
312 pub name: String,
314 pub access: AccessFlags,
316 pub descriptor: String,
318 pub generic_signature: Option<GenericMethodSignature>,
321 pub annotations: Vec<AnnotationStub>,
323 pub parameter_annotations: Vec<Vec<AnnotationStub>>,
326 pub parameter_names: Vec<String>,
329 pub return_type: TypeSignature,
331 pub parameter_types: Vec<TypeSignature>,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct FieldStub {
338 pub name: String,
340 pub access: AccessFlags,
342 pub descriptor: String,
344 pub generic_signature: Option<TypeSignature>,
347 pub annotations: Vec<AnnotationStub>,
349 pub constant_value: Option<ConstantValue>,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct AnnotationStub {
360 pub type_fqn: String,
363 pub elements: Vec<AnnotationElement>,
365 pub is_runtime_visible: bool,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct AnnotationElement {
373 pub name: String,
375 pub value: AnnotationElementValue,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
381pub enum AnnotationElementValue {
382 Const(ConstantValue),
384 EnumConst {
386 type_fqn: String,
388 const_name: String,
390 },
391 ClassInfo(String),
393 Annotation(Box<AnnotationStub>),
395 Array(Vec<AnnotationElementValue>),
397}
398
399#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct GenericClassSignature {
409 pub type_parameters: Vec<TypeParameterStub>,
411 pub superclass: TypeSignature,
413 pub interfaces: Vec<TypeSignature>,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct GenericMethodSignature {
423 pub type_parameters: Vec<TypeParameterStub>,
425 pub parameter_types: Vec<TypeSignature>,
427 pub return_type: TypeSignature,
429 pub exception_types: Vec<TypeSignature>,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct TypeParameterStub {
436 pub name: String,
438 pub class_bound: Option<TypeSignature>,
441 pub interface_bounds: Vec<TypeSignature>,
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize)]
452pub enum TypeSignature {
453 Base(BaseType),
456 Class {
458 fqn: String,
460 type_arguments: Vec<TypeArgument>,
463 },
464 TypeVariable(String),
466 Array(Box<TypeSignature>),
468 Wildcard {
470 bound: Option<WildcardBound>,
472 },
473}
474
475#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
477pub enum BaseType {
478 Byte,
480 Char,
482 Double,
484 Float,
486 Int,
488 Long,
490 Short,
492 Boolean,
494 Void,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
500pub enum TypeArgument {
501 Type(TypeSignature),
503 Extends(TypeSignature),
505 Super(TypeSignature),
507 Unbounded,
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize)]
513pub enum WildcardBound {
514 Extends(Box<TypeSignature>),
516 Super(Box<TypeSignature>),
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct LambdaTargetStub {
528 pub owner_fqn: String,
530 pub method_name: String,
532 pub method_descriptor: String,
534 pub reference_kind: ReferenceKind,
536}
537
538#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
540pub enum ReferenceKind {
541 GetField,
543 GetStatic,
545 PutField,
547 PutStatic,
549 InvokeVirtual,
551 InvokeStatic,
553 InvokeSpecial,
555 NewInvokeSpecial,
557 InvokeInterface,
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize)]
567pub struct ModuleStub {
568 pub name: String,
570 pub access: AccessFlags,
572 pub version: Option<String>,
574 pub requires: Vec<ModuleRequires>,
576 pub exports: Vec<ModuleExports>,
578 pub opens: Vec<ModuleOpens>,
580 pub provides: Vec<ModuleProvides>,
582 pub uses: Vec<String>,
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize)]
588pub struct ModuleRequires {
589 pub module_name: String,
591 pub access: AccessFlags,
593 pub version: Option<String>,
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct ModuleExports {
600 pub package: String,
602 pub access: AccessFlags,
604 pub to_modules: Vec<String>,
606}
607
608#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct ModuleOpens {
611 pub package: String,
613 pub access: AccessFlags,
615 pub to_modules: Vec<String>,
617}
618
619#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct ModuleProvides {
622 pub service: String,
624 pub implementations: Vec<String>,
626}
627
628#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct RecordComponent {
635 pub name: String,
637 pub descriptor: String,
639 pub generic_signature: Option<TypeSignature>,
641 pub annotations: Vec<AnnotationStub>,
643}
644
645#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct InnerClassEntry {
652 pub inner_fqn: String,
654 pub outer_fqn: Option<String>,
656 pub inner_name: Option<String>,
658 pub access: AccessFlags,
660}
661
662#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
669pub enum ConstantValue {
670 Int(i32),
672 Long(i64),
674 Float(OrderedFloat<f32>),
676 Double(OrderedFloat<f64>),
678 String(String),
680}
681
682#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
688pub struct OrderedFloat<T>(pub T);
689
690impl PartialEq for OrderedFloat<f32> {
691 fn eq(&self, other: &Self) -> bool {
692 self.0.to_bits() == other.0.to_bits()
693 }
694}
695
696impl Eq for OrderedFloat<f32> {}
697
698impl PartialEq for OrderedFloat<f64> {
699 fn eq(&self, other: &Self) -> bool {
700 self.0.to_bits() == other.0.to_bits()
701 }
702}
703
704impl Eq for OrderedFloat<f64> {}
705
706impl std::hash::Hash for OrderedFloat<f32> {
707 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
708 self.0.to_bits().hash(state);
709 }
710}
711
712impl std::hash::Hash for OrderedFloat<f64> {
713 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
714 self.0.to_bits().hash(state);
715 }
716}
717
718#[derive(Debug, Clone, Serialize, Deserialize)]
727pub struct KotlinMetadataStub {
728 pub kind: u32,
735 pub metadata_version: Vec<u32>,
737 pub data1: Vec<String>,
739 pub data2: Vec<String>,
741 pub extra_string: Option<String>,
743 pub package_name: Option<String>,
745 pub extra_int: Option<i32>,
747}
748
749#[derive(Debug, Clone, Serialize, Deserialize)]
758pub struct ScalaSignatureStub {
759 pub bytes: Vec<u8>,
761 pub major_version: u32,
763 pub minor_version: u32,
765}
766
767#[cfg(test)]
772mod tests {
773 use super::*;
774
775 fn sample_access_flags() -> AccessFlags {
778 AccessFlags::new(AccessFlags::ACC_PUBLIC | AccessFlags::ACC_FINAL | AccessFlags::ACC_STATIC)
779 }
780
781 fn sample_type_signature_class() -> TypeSignature {
782 TypeSignature::Class {
783 fqn: "java.lang.String".to_owned(),
784 type_arguments: vec![],
785 }
786 }
787
788 fn sample_annotation() -> AnnotationStub {
789 AnnotationStub {
790 type_fqn: "java.lang.Override".to_owned(),
791 elements: vec![],
792 is_runtime_visible: true,
793 }
794 }
795
796 fn sample_method_stub() -> MethodStub {
797 MethodStub {
798 name: "toString".to_owned(),
799 access: AccessFlags::new(AccessFlags::ACC_PUBLIC),
800 descriptor: "()Ljava/lang/String;".to_owned(),
801 generic_signature: None,
802 annotations: vec![sample_annotation()],
803 parameter_annotations: vec![],
804 parameter_names: vec![],
805 return_type: sample_type_signature_class(),
806 parameter_types: vec![],
807 }
808 }
809
810 fn sample_field_stub() -> FieldStub {
811 FieldStub {
812 name: "MAX_SIZE".to_owned(),
813 access: AccessFlags::new(
814 AccessFlags::ACC_PUBLIC | AccessFlags::ACC_STATIC | AccessFlags::ACC_FINAL,
815 ),
816 descriptor: "I".to_owned(),
817 generic_signature: None,
818 annotations: vec![],
819 constant_value: Some(ConstantValue::Int(1024)),
820 }
821 }
822
823 fn sample_class_stub() -> ClassStub {
824 ClassStub {
825 fqn: "com.example.MyClass".to_owned(),
826 name: "MyClass".to_owned(),
827 kind: ClassKind::Class,
828 access: AccessFlags::new(AccessFlags::ACC_PUBLIC),
829 superclass: Some("java.lang.Object".to_owned()),
830 interfaces: vec!["java.io.Serializable".to_owned()],
831 methods: vec![sample_method_stub()],
832 fields: vec![sample_field_stub()],
833 annotations: vec![],
834 generic_signature: None,
835 inner_classes: vec![],
836 lambda_targets: vec![],
837 module: None,
838 record_components: vec![],
839 enum_constants: vec![],
840 source_file: Some("MyClass.java".to_owned()),
841 source_jar: None,
842 kotlin_metadata: None,
843 scala_signature: None,
844 }
845 }
846
847 fn round_trip<T: Serialize + for<'de> Deserialize<'de> + std::fmt::Debug>(value: &T) -> T {
850 let bytes = postcard::to_allocvec(value).expect("postcard serialization should succeed");
851 postcard::from_bytes(&bytes).expect("postcard deserialization should succeed")
852 }
853
854 #[test]
857 fn access_flags_individual_bits() {
858 let flags = AccessFlags::new(AccessFlags::ACC_PUBLIC);
859 assert!(flags.is_public());
860 assert!(!flags.is_private());
861 assert!(!flags.is_protected());
862 assert!(!flags.is_static());
863 assert!(!flags.is_final());
864 assert!(!flags.is_abstract());
865 assert!(!flags.is_synthetic());
866 }
867
868 #[test]
869 fn access_flags_combined_bits() {
870 let flags = sample_access_flags();
871 assert!(flags.is_public());
872 assert!(flags.is_final());
873 assert!(flags.is_static());
874 assert!(!flags.is_private());
875 assert!(!flags.is_abstract());
876 }
877
878 #[test]
879 fn access_flags_empty() {
880 let flags = AccessFlags::empty();
881 assert_eq!(flags.bits(), 0);
882 assert!(!flags.is_public());
883 assert!(!flags.is_private());
884 }
885
886 #[test]
887 fn access_flags_all_class_flags() {
888 let flags = AccessFlags::new(
889 AccessFlags::ACC_PUBLIC
890 | AccessFlags::ACC_INTERFACE
891 | AccessFlags::ACC_ABSTRACT
892 | AccessFlags::ACC_ANNOTATION,
893 );
894 assert!(flags.is_public());
895 assert!(flags.is_interface());
896 assert!(flags.is_abstract());
897 assert!(flags.is_annotation());
898 assert!(!flags.is_enum());
899 assert!(!flags.is_module());
900 }
901
902 #[test]
903 fn access_flags_contains() {
904 let flags = AccessFlags::new(AccessFlags::ACC_PUBLIC | AccessFlags::ACC_STATIC);
905 assert!(flags.contains(AccessFlags::ACC_PUBLIC));
906 assert!(flags.contains(AccessFlags::ACC_STATIC));
907 assert!(flags.contains(AccessFlags::ACC_PUBLIC | AccessFlags::ACC_STATIC));
908 assert!(!flags.contains(AccessFlags::ACC_FINAL));
909 }
910
911 #[test]
912 fn access_flags_union() {
913 let a = AccessFlags::new(AccessFlags::ACC_PUBLIC);
914 let b = AccessFlags::new(AccessFlags::ACC_STATIC);
915 let combined = a.union(b);
916 assert!(combined.is_public());
917 assert!(combined.is_static());
918 assert_eq!(
919 combined.bits(),
920 AccessFlags::ACC_PUBLIC | AccessFlags::ACC_STATIC
921 );
922 }
923
924 #[test]
925 fn access_flags_display() {
926 let flags = AccessFlags::new(AccessFlags::ACC_PUBLIC | AccessFlags::ACC_STATIC);
927 let display = format!("{flags}");
928 assert!(display.contains("public"));
929 assert!(display.contains("static"));
930 }
931
932 #[test]
933 fn access_flags_method_specific() {
934 let flags = AccessFlags::new(
935 AccessFlags::ACC_PUBLIC
936 | AccessFlags::ACC_SYNCHRONIZED
937 | AccessFlags::ACC_BRIDGE
938 | AccessFlags::ACC_VARARGS
939 | AccessFlags::ACC_NATIVE,
940 );
941 assert!(flags.is_public());
942 assert!(flags.is_synchronized());
943 assert!(flags.is_bridge());
944 assert!(flags.is_varargs());
945 assert!(flags.is_native());
946 }
947
948 #[test]
949 fn access_flags_field_specific() {
950 let flags = AccessFlags::new(
951 AccessFlags::ACC_PRIVATE | AccessFlags::ACC_VOLATILE | AccessFlags::ACC_TRANSIENT,
952 );
953 assert!(flags.is_private());
954 assert!(flags.is_volatile());
955 assert!(flags.is_transient());
956 }
957
958 #[test]
959 fn access_flags_round_trip() {
960 let flags = sample_access_flags();
961 let rt = round_trip(&flags);
962 assert_eq!(flags.bits(), rt.bits());
963 }
964
965 #[test]
968 fn class_kind_round_trip() {
969 for kind in [
970 ClassKind::Class,
971 ClassKind::Interface,
972 ClassKind::Enum,
973 ClassKind::Annotation,
974 ClassKind::Record,
975 ClassKind::Module,
976 ] {
977 let rt = round_trip(&kind);
978 assert_eq!(kind, rt);
979 }
980 }
981
982 #[test]
985 fn class_stub_round_trip() {
986 let stub = sample_class_stub();
987 let rt = round_trip(&stub);
988 assert_eq!(stub.fqn, rt.fqn);
989 assert_eq!(stub.name, rt.name);
990 assert_eq!(stub.kind, rt.kind);
991 assert_eq!(stub.access.bits(), rt.access.bits());
992 assert_eq!(stub.superclass, rt.superclass);
993 assert_eq!(stub.interfaces, rt.interfaces);
994 assert_eq!(stub.methods.len(), rt.methods.len());
995 assert_eq!(stub.fields.len(), rt.fields.len());
996 assert_eq!(stub.source_file, rt.source_file);
997 }
998
999 #[test]
1002 fn method_stub_round_trip() {
1003 let method = sample_method_stub();
1004 let rt = round_trip(&method);
1005 assert_eq!(method.name, rt.name);
1006 assert_eq!(method.descriptor, rt.descriptor);
1007 assert_eq!(method.access.bits(), rt.access.bits());
1008 assert_eq!(method.annotations.len(), rt.annotations.len());
1009 }
1010
1011 #[test]
1014 fn field_stub_round_trip() {
1015 let field = sample_field_stub();
1016 let rt = round_trip(&field);
1017 assert_eq!(field.name, rt.name);
1018 assert_eq!(field.descriptor, rt.descriptor);
1019 assert_eq!(field.access.bits(), rt.access.bits());
1020 assert_eq!(field.constant_value, rt.constant_value);
1021 }
1022
1023 #[test]
1026 fn constant_value_int_round_trip() {
1027 let cv = ConstantValue::Int(42);
1028 assert_eq!(cv, round_trip(&cv));
1029 }
1030
1031 #[test]
1032 fn constant_value_long_round_trip() {
1033 let cv = ConstantValue::Long(i64::MAX);
1034 assert_eq!(cv, round_trip(&cv));
1035 }
1036
1037 #[test]
1038 fn constant_value_float_round_trip() {
1039 let cv = ConstantValue::Float(OrderedFloat(std::f32::consts::PI));
1040 assert_eq!(cv, round_trip(&cv));
1041 }
1042
1043 #[test]
1044 fn constant_value_double_round_trip() {
1045 let cv = ConstantValue::Double(OrderedFloat(std::f64::consts::E));
1046 assert_eq!(cv, round_trip(&cv));
1047 }
1048
1049 #[test]
1050 fn constant_value_string_round_trip() {
1051 let cv = ConstantValue::String("hello world".to_owned());
1052 assert_eq!(cv, round_trip(&cv));
1053 }
1054
1055 #[test]
1058 fn annotation_simple_round_trip() {
1059 let ann = sample_annotation();
1060 let rt = round_trip(&ann);
1061 assert_eq!(ann.type_fqn, rt.type_fqn);
1062 assert_eq!(ann.is_runtime_visible, rt.is_runtime_visible);
1063 }
1064
1065 #[test]
1068 fn annotation_nested_round_trip() {
1069 let inner = AnnotationStub {
1070 type_fqn: "javax.validation.constraints.Size".to_owned(),
1071 elements: vec![
1072 AnnotationElement {
1073 name: "min".to_owned(),
1074 value: AnnotationElementValue::Const(ConstantValue::Int(1)),
1075 },
1076 AnnotationElement {
1077 name: "max".to_owned(),
1078 value: AnnotationElementValue::Const(ConstantValue::Int(100)),
1079 },
1080 ],
1081 is_runtime_visible: true,
1082 };
1083
1084 let outer = AnnotationStub {
1085 type_fqn: "javax.validation.constraints.NotNull".to_owned(),
1086 elements: vec![AnnotationElement {
1087 name: "payload".to_owned(),
1088 value: AnnotationElementValue::Annotation(Box::new(inner)),
1089 }],
1090 is_runtime_visible: true,
1091 };
1092
1093 let rt = round_trip(&outer);
1094 assert_eq!(outer.type_fqn, rt.type_fqn);
1095 assert_eq!(outer.elements.len(), rt.elements.len());
1096 match &rt.elements[0].value {
1097 AnnotationElementValue::Annotation(inner_rt) => {
1098 assert_eq!(inner_rt.type_fqn, "javax.validation.constraints.Size");
1099 assert_eq!(inner_rt.elements.len(), 2);
1100 }
1101 other => panic!("expected nested Annotation, got {other:?}"),
1102 }
1103 }
1104
1105 #[test]
1108 fn annotation_complex_elements_round_trip() {
1109 let ann = AnnotationStub {
1110 type_fqn: "org.springframework.web.bind.annotation.RequestMapping".to_owned(),
1111 elements: vec![
1112 AnnotationElement {
1113 name: "value".to_owned(),
1114 value: AnnotationElementValue::Array(vec![AnnotationElementValue::Const(
1115 ConstantValue::String("/api/users".to_owned()),
1116 )]),
1117 },
1118 AnnotationElement {
1119 name: "method".to_owned(),
1120 value: AnnotationElementValue::Array(vec![
1121 AnnotationElementValue::EnumConst {
1122 type_fqn: "org.springframework.web.bind.annotation.RequestMethod"
1123 .to_owned(),
1124 const_name: "GET".to_owned(),
1125 },
1126 AnnotationElementValue::EnumConst {
1127 type_fqn: "org.springframework.web.bind.annotation.RequestMethod"
1128 .to_owned(),
1129 const_name: "POST".to_owned(),
1130 },
1131 ]),
1132 },
1133 AnnotationElement {
1134 name: "produces".to_owned(),
1135 value: AnnotationElementValue::ClassInfo("Ljava/lang/String;".to_owned()),
1136 },
1137 ],
1138 is_runtime_visible: true,
1139 };
1140
1141 let rt = round_trip(&ann);
1142 assert_eq!(ann.elements.len(), rt.elements.len());
1143 assert_eq!(ann.type_fqn, rt.type_fqn);
1144 }
1145
1146 #[test]
1149 fn generic_class_signature_round_trip() {
1150 let sig = GenericClassSignature {
1152 type_parameters: vec![
1153 TypeParameterStub {
1154 name: "K".to_owned(),
1155 class_bound: Some(TypeSignature::Class {
1156 fqn: "java.lang.Comparable".to_owned(),
1157 type_arguments: vec![TypeArgument::Type(TypeSignature::TypeVariable(
1158 "K".to_owned(),
1159 ))],
1160 }),
1161 interface_bounds: vec![],
1162 },
1163 TypeParameterStub {
1164 name: "V".to_owned(),
1165 class_bound: None,
1166 interface_bounds: vec![],
1167 },
1168 ],
1169 superclass: TypeSignature::Class {
1170 fqn: "java.util.HashMap".to_owned(),
1171 type_arguments: vec![
1172 TypeArgument::Type(TypeSignature::TypeVariable("K".to_owned())),
1173 TypeArgument::Type(TypeSignature::Class {
1174 fqn: "java.util.List".to_owned(),
1175 type_arguments: vec![TypeArgument::Type(TypeSignature::TypeVariable(
1176 "V".to_owned(),
1177 ))],
1178 }),
1179 ],
1180 },
1181 interfaces: vec![TypeSignature::Class {
1182 fqn: "java.io.Serializable".to_owned(),
1183 type_arguments: vec![],
1184 }],
1185 };
1186
1187 let rt = round_trip(&sig);
1188 assert_eq!(sig.type_parameters.len(), rt.type_parameters.len());
1189 assert_eq!(sig.type_parameters[0].name, rt.type_parameters[0].name);
1190 assert_eq!(sig.interfaces.len(), rt.interfaces.len());
1191 }
1192
1193 #[test]
1194 fn generic_method_signature_round_trip() {
1195 let sig = GenericMethodSignature {
1197 type_parameters: vec![TypeParameterStub {
1198 name: "T".to_owned(),
1199 class_bound: Some(TypeSignature::Class {
1200 fqn: "java.lang.Number".to_owned(),
1201 type_arguments: vec![],
1202 }),
1203 interface_bounds: vec![],
1204 }],
1205 parameter_types: vec![TypeSignature::Class {
1206 fqn: "java.util.Collection".to_owned(),
1207 type_arguments: vec![TypeArgument::Extends(TypeSignature::TypeVariable(
1208 "T".to_owned(),
1209 ))],
1210 }],
1211 return_type: TypeSignature::Class {
1212 fqn: "java.util.List".to_owned(),
1213 type_arguments: vec![TypeArgument::Type(TypeSignature::TypeVariable(
1214 "T".to_owned(),
1215 ))],
1216 },
1217 exception_types: vec![TypeSignature::Class {
1218 fqn: "java.lang.Exception".to_owned(),
1219 type_arguments: vec![],
1220 }],
1221 };
1222
1223 let rt = round_trip(&sig);
1224 assert_eq!(sig.type_parameters.len(), rt.type_parameters.len());
1225 assert_eq!(sig.parameter_types.len(), rt.parameter_types.len());
1226 assert_eq!(sig.exception_types.len(), rt.exception_types.len());
1227 }
1228
1229 #[test]
1232 fn type_signature_base_round_trip() {
1233 for base in [
1234 BaseType::Byte,
1235 BaseType::Char,
1236 BaseType::Double,
1237 BaseType::Float,
1238 BaseType::Int,
1239 BaseType::Long,
1240 BaseType::Short,
1241 BaseType::Boolean,
1242 BaseType::Void,
1243 ] {
1244 let sig = TypeSignature::Base(base);
1245 let bytes = postcard::to_allocvec(&sig).expect("serialization should succeed");
1246 let rt: TypeSignature =
1247 postcard::from_bytes(&bytes).expect("deserialization should succeed");
1248 match (&sig, &rt) {
1249 (TypeSignature::Base(a), TypeSignature::Base(b)) => assert_eq!(a, b),
1250 _ => panic!("expected Base variant"),
1251 }
1252 }
1253 }
1254
1255 #[test]
1256 fn type_signature_array_round_trip() {
1257 let sig = TypeSignature::Array(Box::new(TypeSignature::Array(Box::new(
1259 TypeSignature::Class {
1260 fqn: "java.lang.String".to_owned(),
1261 type_arguments: vec![],
1262 },
1263 ))));
1264 let rt = round_trip(&sig);
1265 match rt {
1266 TypeSignature::Array(inner) => match *inner {
1267 TypeSignature::Array(inner2) => match *inner2 {
1268 TypeSignature::Class { ref fqn, .. } => {
1269 assert_eq!(fqn, "java.lang.String");
1270 }
1271 _ => panic!("expected Class"),
1272 },
1273 _ => panic!("expected nested Array"),
1274 },
1275 _ => panic!("expected Array"),
1276 }
1277 }
1278
1279 #[test]
1280 fn type_signature_wildcard_round_trip() {
1281 let sig = TypeSignature::Wildcard {
1282 bound: Some(WildcardBound::Extends(Box::new(TypeSignature::Class {
1283 fqn: "java.lang.Number".to_owned(),
1284 type_arguments: vec![],
1285 }))),
1286 };
1287 let rt = round_trip(&sig);
1288 match rt {
1289 TypeSignature::Wildcard {
1290 bound: Some(WildcardBound::Extends(inner)),
1291 } => match *inner {
1292 TypeSignature::Class { ref fqn, .. } => {
1293 assert_eq!(fqn, "java.lang.Number");
1294 }
1295 _ => panic!("expected Class inside wildcard bound"),
1296 },
1297 _ => panic!("expected Wildcard with Extends bound"),
1298 }
1299 }
1300
1301 #[test]
1304 fn type_argument_variants_round_trip() {
1305 let args = vec![
1306 TypeArgument::Type(TypeSignature::Base(BaseType::Int)),
1307 TypeArgument::Extends(TypeSignature::Class {
1308 fqn: "java.lang.Number".to_owned(),
1309 type_arguments: vec![],
1310 }),
1311 TypeArgument::Super(TypeSignature::Class {
1312 fqn: "java.lang.Integer".to_owned(),
1313 type_arguments: vec![],
1314 }),
1315 TypeArgument::Unbounded,
1316 ];
1317 let rt = round_trip(&args);
1318 assert_eq!(args.len(), rt.len());
1319 }
1320
1321 #[test]
1324 fn reference_kind_round_trip() {
1325 for kind in [
1326 ReferenceKind::GetField,
1327 ReferenceKind::GetStatic,
1328 ReferenceKind::PutField,
1329 ReferenceKind::PutStatic,
1330 ReferenceKind::InvokeVirtual,
1331 ReferenceKind::InvokeStatic,
1332 ReferenceKind::InvokeSpecial,
1333 ReferenceKind::NewInvokeSpecial,
1334 ReferenceKind::InvokeInterface,
1335 ] {
1336 let rt = round_trip(&kind);
1337 assert_eq!(kind, rt);
1338 }
1339 }
1340
1341 #[test]
1344 fn lambda_target_round_trip() {
1345 let target = LambdaTargetStub {
1346 owner_fqn: "java.util.stream.Stream".to_owned(),
1347 method_name: "map".to_owned(),
1348 method_descriptor: "(Ljava/util/function/Function;)Ljava/util/stream/Stream;"
1349 .to_owned(),
1350 reference_kind: ReferenceKind::InvokeInterface,
1351 };
1352 let rt = round_trip(&target);
1353 assert_eq!(target.owner_fqn, rt.owner_fqn);
1354 assert_eq!(target.method_name, rt.method_name);
1355 assert_eq!(target.method_descriptor, rt.method_descriptor);
1356 assert_eq!(target.reference_kind, rt.reference_kind);
1357 }
1358
1359 #[test]
1362 fn module_stub_round_trip() {
1363 let module = ModuleStub {
1364 name: "java.base".to_owned(),
1365 access: AccessFlags::new(AccessFlags::ACC_MODULE),
1366 version: Some("17".to_owned()),
1367 requires: vec![ModuleRequires {
1368 module_name: "java.logging".to_owned(),
1369 access: AccessFlags::empty(),
1370 version: None,
1371 }],
1372 exports: vec![ModuleExports {
1373 package: "java.lang".to_owned(),
1374 access: AccessFlags::empty(),
1375 to_modules: vec![],
1376 }],
1377 opens: vec![ModuleOpens {
1378 package: "java.lang.invoke".to_owned(),
1379 access: AccessFlags::empty(),
1380 to_modules: vec!["jdk.internal.vm.ci".to_owned()],
1381 }],
1382 provides: vec![ModuleProvides {
1383 service: "java.security.Provider".to_owned(),
1384 implementations: vec!["sun.security.provider.Sun".to_owned()],
1385 }],
1386 uses: vec!["java.security.Provider".to_owned()],
1387 };
1388 let rt = round_trip(&module);
1389 assert_eq!(module.name, rt.name);
1390 assert_eq!(module.requires.len(), rt.requires.len());
1391 assert_eq!(module.exports.len(), rt.exports.len());
1392 assert_eq!(module.opens.len(), rt.opens.len());
1393 assert_eq!(module.provides.len(), rt.provides.len());
1394 assert_eq!(module.uses, rt.uses);
1395 }
1396
1397 #[test]
1400 fn record_component_round_trip() {
1401 let comp = RecordComponent {
1402 name: "name".to_owned(),
1403 descriptor: "Ljava/lang/String;".to_owned(),
1404 generic_signature: None,
1405 annotations: vec![AnnotationStub {
1406 type_fqn: "javax.annotation.Nonnull".to_owned(),
1407 elements: vec![],
1408 is_runtime_visible: true,
1409 }],
1410 };
1411 let rt = round_trip(&comp);
1412 assert_eq!(comp.name, rt.name);
1413 assert_eq!(comp.descriptor, rt.descriptor);
1414 assert_eq!(comp.annotations.len(), rt.annotations.len());
1415 }
1416
1417 #[test]
1420 fn inner_class_entry_round_trip() {
1421 let entry = InnerClassEntry {
1422 inner_fqn: "com.example.Outer.Inner".to_owned(),
1423 outer_fqn: Some("com.example.Outer".to_owned()),
1424 inner_name: Some("Inner".to_owned()),
1425 access: AccessFlags::new(AccessFlags::ACC_PUBLIC | AccessFlags::ACC_STATIC),
1426 };
1427 let rt = round_trip(&entry);
1428 assert_eq!(entry.inner_fqn, rt.inner_fqn);
1429 assert_eq!(entry.outer_fqn, rt.outer_fqn);
1430 assert_eq!(entry.inner_name, rt.inner_name);
1431 assert_eq!(entry.access.bits(), rt.access.bits());
1432 }
1433
1434 #[test]
1435 fn inner_class_anonymous_round_trip() {
1436 let entry = InnerClassEntry {
1437 inner_fqn: "com.example.Outer$1".to_owned(),
1438 outer_fqn: None,
1439 inner_name: None,
1440 access: AccessFlags::empty(),
1441 };
1442 let rt = round_trip(&entry);
1443 assert_eq!(entry.inner_fqn, rt.inner_fqn);
1444 assert!(rt.outer_fqn.is_none());
1445 assert!(rt.inner_name.is_none());
1446 }
1447
1448 #[test]
1451 fn kotlin_metadata_round_trip() {
1452 let meta = KotlinMetadataStub {
1453 kind: 1,
1454 metadata_version: vec![1, 9, 0],
1455 data1: vec!["proto_data_chunk_1".to_owned()],
1456 data2: vec!["string_table_entry".to_owned()],
1457 extra_string: Some("com/example/MyClass".to_owned()),
1458 package_name: Some("com.example".to_owned()),
1459 extra_int: Some(50),
1460 };
1461 let rt = round_trip(&meta);
1462 assert_eq!(meta.kind, rt.kind);
1463 assert_eq!(meta.metadata_version, rt.metadata_version);
1464 assert_eq!(meta.data1, rt.data1);
1465 assert_eq!(meta.data2, rt.data2);
1466 assert_eq!(meta.extra_string, rt.extra_string);
1467 assert_eq!(meta.package_name, rt.package_name);
1468 assert_eq!(meta.extra_int, rt.extra_int);
1469 }
1470
1471 #[test]
1474 fn scala_signature_round_trip() {
1475 let sig = ScalaSignatureStub {
1476 bytes: vec![0x05, 0x00, 0x01, 0x09, 0x02],
1477 major_version: 5,
1478 minor_version: 0,
1479 };
1480 let rt = round_trip(&sig);
1481 assert_eq!(sig.bytes, rt.bytes);
1482 assert_eq!(sig.major_version, rt.major_version);
1483 assert_eq!(sig.minor_version, rt.minor_version);
1484 }
1485
1486 #[test]
1489 fn class_stub_fully_populated_round_trip() {
1490 let stub = ClassStub {
1491 fqn: "com.example.FullClass".to_owned(),
1492 name: "FullClass".to_owned(),
1493 kind: ClassKind::Record,
1494 access: AccessFlags::new(AccessFlags::ACC_PUBLIC | AccessFlags::ACC_FINAL),
1495 superclass: Some("java.lang.Record".to_owned()),
1496 interfaces: vec![
1497 "java.io.Serializable".to_owned(),
1498 "java.lang.Comparable".to_owned(),
1499 ],
1500 methods: vec![sample_method_stub()],
1501 fields: vec![sample_field_stub()],
1502 annotations: vec![AnnotationStub {
1503 type_fqn: "java.lang.Deprecated".to_owned(),
1504 elements: vec![AnnotationElement {
1505 name: "since".to_owned(),
1506 value: AnnotationElementValue::Const(ConstantValue::String("17".to_owned())),
1507 }],
1508 is_runtime_visible: true,
1509 }],
1510 generic_signature: Some(GenericClassSignature {
1511 type_parameters: vec![TypeParameterStub {
1512 name: "T".to_owned(),
1513 class_bound: Some(TypeSignature::Class {
1514 fqn: "java.lang.Number".to_owned(),
1515 type_arguments: vec![],
1516 }),
1517 interface_bounds: vec![],
1518 }],
1519 superclass: TypeSignature::Class {
1520 fqn: "java.lang.Record".to_owned(),
1521 type_arguments: vec![],
1522 },
1523 interfaces: vec![],
1524 }),
1525 inner_classes: vec![InnerClassEntry {
1526 inner_fqn: "com.example.FullClass.Builder".to_owned(),
1527 outer_fqn: Some("com.example.FullClass".to_owned()),
1528 inner_name: Some("Builder".to_owned()),
1529 access: AccessFlags::new(AccessFlags::ACC_PUBLIC | AccessFlags::ACC_STATIC),
1530 }],
1531 lambda_targets: vec![LambdaTargetStub {
1532 owner_fqn: "com.example.FullClass".to_owned(),
1533 method_name: "lambda$process$0".to_owned(),
1534 method_descriptor: "(Ljava/lang/Object;)V".to_owned(),
1535 reference_kind: ReferenceKind::InvokeStatic,
1536 }],
1537 module: None,
1538 record_components: vec![RecordComponent {
1539 name: "value".to_owned(),
1540 descriptor: "Ljava/lang/Number;".to_owned(),
1541 generic_signature: Some(TypeSignature::TypeVariable("T".to_owned())),
1542 annotations: vec![],
1543 }],
1544 enum_constants: vec![],
1545 source_file: Some("FullClass.java".to_owned()),
1546 source_jar: Some("/jars/full.jar".to_owned()),
1547 kotlin_metadata: Some(KotlinMetadataStub {
1548 kind: 1,
1549 metadata_version: vec![1, 9, 0],
1550 data1: vec![],
1551 data2: vec![],
1552 extra_string: None,
1553 package_name: None,
1554 extra_int: None,
1555 }),
1556 scala_signature: Some(ScalaSignatureStub {
1557 bytes: vec![0x05, 0x00],
1558 major_version: 5,
1559 minor_version: 0,
1560 }),
1561 };
1562
1563 let bytes = postcard::to_allocvec(&stub).expect("serialization should succeed");
1564 assert!(!bytes.is_empty(), "serialized bytes should not be empty");
1565 let rt: ClassStub = postcard::from_bytes(&bytes).expect("deserialization should succeed");
1566 assert_eq!(stub.fqn, rt.fqn);
1567 assert_eq!(stub.kind, rt.kind);
1568 assert_eq!(stub.methods.len(), rt.methods.len());
1569 assert_eq!(stub.fields.len(), rt.fields.len());
1570 assert_eq!(stub.inner_classes.len(), rt.inner_classes.len());
1571 assert_eq!(stub.lambda_targets.len(), rt.lambda_targets.len());
1572 assert_eq!(stub.record_components.len(), rt.record_components.len());
1573 assert!(rt.generic_signature.is_some());
1574 assert!(rt.kotlin_metadata.is_some());
1575 assert!(rt.scala_signature.is_some());
1576 }
1577
1578 #[test]
1581 fn class_stub_module_round_trip() {
1582 let stub = ClassStub {
1583 fqn: "module-info".to_owned(),
1584 name: "module-info".to_owned(),
1585 kind: ClassKind::Module,
1586 access: AccessFlags::new(AccessFlags::ACC_MODULE),
1587 superclass: None,
1588 interfaces: vec![],
1589 methods: vec![],
1590 fields: vec![],
1591 annotations: vec![],
1592 generic_signature: None,
1593 inner_classes: vec![],
1594 lambda_targets: vec![],
1595 module: Some(ModuleStub {
1596 name: "com.example.app".to_owned(),
1597 access: AccessFlags::new(AccessFlags::ACC_MODULE),
1598 version: Some("1.0".to_owned()),
1599 requires: vec![
1600 ModuleRequires {
1601 module_name: "java.base".to_owned(),
1602 access: AccessFlags::empty(),
1603 version: Some("17".to_owned()),
1604 },
1605 ModuleRequires {
1606 module_name: "java.sql".to_owned(),
1607 access: AccessFlags::empty(),
1608 version: None,
1609 },
1610 ],
1611 exports: vec![ModuleExports {
1612 package: "com.example.api".to_owned(),
1613 access: AccessFlags::empty(),
1614 to_modules: vec![],
1615 }],
1616 opens: vec![],
1617 provides: vec![],
1618 uses: vec![],
1619 }),
1620 record_components: vec![],
1621 enum_constants: vec![],
1622 source_file: None,
1623 source_jar: None,
1624 kotlin_metadata: None,
1625 scala_signature: None,
1626 };
1627
1628 let rt = round_trip(&stub);
1629 assert_eq!(stub.kind, rt.kind);
1630 let module = rt.module.expect("module should be present");
1631 assert_eq!(module.name, "com.example.app");
1632 assert_eq!(module.requires.len(), 2);
1633 }
1634
1635 #[test]
1638 fn class_stub_enum_round_trip() {
1639 let stub = ClassStub {
1640 fqn: "com.example.Color".to_owned(),
1641 name: "Color".to_owned(),
1642 kind: ClassKind::Enum,
1643 access: AccessFlags::new(
1644 AccessFlags::ACC_PUBLIC | AccessFlags::ACC_FINAL | AccessFlags::ACC_ENUM,
1645 ),
1646 superclass: Some("java.lang.Enum".to_owned()),
1647 interfaces: vec![],
1648 methods: vec![],
1649 fields: vec![],
1650 annotations: vec![],
1651 generic_signature: None,
1652 inner_classes: vec![],
1653 lambda_targets: vec![],
1654 module: None,
1655 record_components: vec![],
1656 enum_constants: vec!["RED".to_owned(), "GREEN".to_owned(), "BLUE".to_owned()],
1657 source_file: Some("Color.java".to_owned()),
1658 source_jar: None,
1659 kotlin_metadata: None,
1660 scala_signature: None,
1661 };
1662
1663 let rt = round_trip(&stub);
1664 assert_eq!(stub.kind, rt.kind);
1665 assert_eq!(
1666 stub.enum_constants, rt.enum_constants,
1667 "enum constants should survive round-trip in order"
1668 );
1669 }
1670
1671 #[test]
1674 fn types_are_send_sync() {
1675 fn assert_send_sync<T: Send + Sync>() {}
1676 assert_send_sync::<ClassStub>();
1677 assert_send_sync::<MethodStub>();
1678 assert_send_sync::<FieldStub>();
1679 assert_send_sync::<AnnotationStub>();
1680 assert_send_sync::<GenericClassSignature>();
1681 assert_send_sync::<GenericMethodSignature>();
1682 assert_send_sync::<TypeSignature>();
1683 assert_send_sync::<LambdaTargetStub>();
1684 assert_send_sync::<ModuleStub>();
1685 assert_send_sync::<RecordComponent>();
1686 assert_send_sync::<InnerClassEntry>();
1687 assert_send_sync::<KotlinMetadataStub>();
1688 assert_send_sync::<ScalaSignatureStub>();
1689 assert_send_sync::<ConstantValue>();
1690 assert_send_sync::<AccessFlags>();
1691 assert_send_sync::<ClassKind>();
1692 assert_send_sync::<ReferenceKind>();
1693 assert_send_sync::<BaseType>();
1694 assert_send_sync::<TypeArgument>();
1695 assert_send_sync::<WildcardBound>();
1696 assert_send_sync::<OrderedFloat<f32>>();
1697 assert_send_sync::<OrderedFloat<f64>>();
1698 }
1699}