Skip to main content

sqry_classpath/stub/
model.rs

1//! Stub data model types for parsed JVM bytecode.
2//!
3//! This module defines the intermediate representation produced by bytecode
4//! parsing. Each `.class` file is parsed into a [`ClassStub`] containing
5//! methods, fields, annotations, generic signatures, and JVM-specific metadata
6//! (modules, records, lambdas, Kotlin/Scala metadata).
7//!
8//! All types are pure data — no parsing logic lives here. They are designed for
9//! efficient binary serialization via `postcard` and are `Send + Sync` by
10//! construction (all fields are owned).
11
12use serde::{Deserialize, Serialize};
13
14// ---------------------------------------------------------------------------
15// Core stub types
16// ---------------------------------------------------------------------------
17
18/// Parsed representation of a single `.class` file.
19///
20/// This is the primary unit of bytecode analysis. One `ClassStub` is produced
21/// per `.class` entry in a JAR (or on the filesystem classpath). Stubs are
22/// cached per-JAR and merged into a [`super::ClasspathIndex`] for fast FQN
23/// lookup during graph construction.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ClassStub {
26    /// Fully qualified name (e.g., `"java.util.HashMap"`).
27    pub fqn: String,
28    /// Simple name (e.g., `"HashMap"`).
29    pub name: String,
30    /// Kind of class (class, interface, enum, annotation, record, module).
31    pub kind: ClassKind,
32    /// Access flags (public, final, abstract, etc.).
33    pub access: AccessFlags,
34    /// Superclass FQN (`None` for `java.lang.Object`).
35    pub superclass: Option<String>,
36    /// Implemented interface FQNs.
37    pub interfaces: Vec<String>,
38    /// Methods declared in this class.
39    pub methods: Vec<MethodStub>,
40    /// Fields declared in this class.
41    pub fields: Vec<FieldStub>,
42    /// Class-level annotations.
43    pub annotations: Vec<AnnotationStub>,
44    /// Generic signature (if the class has type parameters or parameterized
45    /// super types).
46    pub generic_signature: Option<GenericClassSignature>,
47    /// Inner class entries from the `InnerClasses` attribute.
48    pub inner_classes: Vec<InnerClassEntry>,
49    /// Lambda / method-reference targets extracted from `BootstrapMethods`.
50    pub lambda_targets: Vec<LambdaTargetStub>,
51    /// Java 9+ module info (only present for `module-info.class`).
52    pub module: Option<ModuleStub>,
53    /// Record components (Java 16+, only for record classes).
54    pub record_components: Vec<RecordComponent>,
55    /// Enum constant names (only for enum classes, in declaration order).
56    pub enum_constants: Vec<String>,
57    /// Source file name from the `SourceFile` attribute.
58    pub source_file: Option<String>,
59    /// Path of the JAR file this class was scanned from.
60    ///
61    /// Set by [`crate::bytecode::scan_jar`] during JAR scanning. `None` for
62    /// classes parsed from loose `.class` files or deserialized from cache
63    /// entries that predate this field.
64    #[serde(default)]
65    pub source_jar: Option<String>,
66    /// Kotlin metadata (if `@kotlin.Metadata` annotation is present).
67    pub kotlin_metadata: Option<KotlinMetadataStub>,
68    /// Scala signature (if `@ScalaSignature` annotation is present).
69    pub scala_signature: Option<ScalaSignatureStub>,
70}
71
72/// Discriminates the kind of a class-file entity.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
74pub enum ClassKind {
75    /// Regular class.
76    Class,
77    /// Interface (including functional interfaces).
78    Interface,
79    /// Enum class.
80    Enum,
81    /// Annotation type (`@interface`).
82    Annotation,
83    /// Record class (Java 16+).
84    Record,
85    /// Module descriptor (`module-info.class`).
86    Module,
87}
88
89// ---------------------------------------------------------------------------
90// Access flags
91// ---------------------------------------------------------------------------
92
93/// Bitflag-style JVM access modifiers (JVMS Table 4.1-A/B).
94///
95/// Stored as a raw `u16` to match the bytecode representation exactly. Helper
96/// methods provide convenient boolean queries.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
98pub struct AccessFlags {
99    bits: u16,
100}
101
102impl AccessFlags {
103    // -- Constants matching the JVM spec (JVMS 4.1, 4.5, 4.6) ---------------
104
105    /// `public`
106    pub const ACC_PUBLIC: u16 = 0x0001;
107    /// `private`
108    pub const ACC_PRIVATE: u16 = 0x0002;
109    /// `protected`
110    pub const ACC_PROTECTED: u16 = 0x0004;
111    /// `static`
112    pub const ACC_STATIC: u16 = 0x0008;
113    /// `final`
114    pub const ACC_FINAL: u16 = 0x0010;
115    /// `synchronized` (methods) / `super` (classes — always set since Java 1.1)
116    pub const ACC_SYNCHRONIZED: u16 = 0x0020;
117    /// Alias for [`Self::ACC_SYNCHRONIZED`] in class context.
118    pub const ACC_SUPER: u16 = 0x0020;
119    /// `volatile` (fields) / bridge method (methods)
120    pub const ACC_VOLATILE: u16 = 0x0040;
121    /// Alias for [`Self::ACC_VOLATILE`] in method context.
122    pub const ACC_BRIDGE: u16 = 0x0040;
123    /// `transient` (fields) / varargs (methods)
124    pub const ACC_TRANSIENT: u16 = 0x0080;
125    /// Alias for [`Self::ACC_TRANSIENT`] in method context.
126    pub const ACC_VARARGS: u16 = 0x0080;
127    /// `native`
128    pub const ACC_NATIVE: u16 = 0x0100;
129    /// `interface`
130    pub const ACC_INTERFACE: u16 = 0x0200;
131    /// `abstract`
132    pub const ACC_ABSTRACT: u16 = 0x0400;
133    /// `strictfp`
134    pub const ACC_STRICT: u16 = 0x0800;
135    /// Compiler-generated (not present in source)
136    pub const ACC_SYNTHETIC: u16 = 0x1000;
137    /// Annotation type
138    pub const ACC_ANNOTATION: u16 = 0x2000;
139    /// Enum class or enum constant field
140    pub const ACC_ENUM: u16 = 0x4000;
141    /// Module (Java 9+)
142    pub const ACC_MODULE: u16 = 0x8000;
143
144    // -- Construction --------------------------------------------------------
145
146    /// Create from raw bits.
147    #[must_use]
148    pub const fn new(bits: u16) -> Self {
149        Self { bits }
150    }
151
152    /// Empty flags (package-private, no modifiers).
153    #[must_use]
154    pub const fn empty() -> Self {
155        Self { bits: 0 }
156    }
157
158    // -- Query methods -------------------------------------------------------
159
160    #[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    /// Raw bit representation.
251    #[must_use]
252    pub const fn bits(&self) -> u16 {
253        self.bits
254    }
255
256    /// Returns `true` if the given flag bit(s) are set.
257    #[must_use]
258    pub const fn contains(&self, flag: u16) -> bool {
259        self.bits & flag == flag
260    }
261
262    /// Combine two flag sets (bitwise OR).
263    #[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// ---------------------------------------------------------------------------
306// Method and field stubs
307// ---------------------------------------------------------------------------
308
309/// Parsed method declaration from a `.class` file.
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct MethodStub {
312    /// Method name (e.g., `"hashCode"`, `"<init>"`, `"<clinit>"`).
313    pub name: String,
314    /// Access flags.
315    pub access: AccessFlags,
316    /// JVM method descriptor (e.g., `"(Ljava/lang/String;)V"`).
317    pub descriptor: String,
318    /// Generic method signature (if the method has type parameters or
319    /// parameterized types not expressible in the descriptor).
320    pub generic_signature: Option<GenericMethodSignature>,
321    /// Method-level annotations.
322    pub annotations: Vec<AnnotationStub>,
323    /// Parameter annotations. Outer vec is indexed by parameter position;
324    /// inner vec contains the annotations for that parameter.
325    pub parameter_annotations: Vec<Vec<AnnotationStub>>,
326    /// Parameter names from the `MethodParameters` attribute (may be empty
327    /// if compiled without `-parameters`).
328    pub parameter_names: Vec<String>,
329    /// Return type parsed from the descriptor.
330    pub return_type: TypeSignature,
331    /// Parameter types parsed from the descriptor.
332    pub parameter_types: Vec<TypeSignature>,
333}
334
335/// Parsed field declaration from a `.class` file.
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct FieldStub {
338    /// Field name.
339    pub name: String,
340    /// Access flags.
341    pub access: AccessFlags,
342    /// JVM field descriptor (e.g., `"Ljava/lang/String;"`).
343    pub descriptor: String,
344    /// Generic field type signature (if the field uses a parameterized type
345    /// not expressible in the descriptor).
346    pub generic_signature: Option<TypeSignature>,
347    /// Field-level annotations.
348    pub annotations: Vec<AnnotationStub>,
349    /// Constant value for `static final` fields of primitive or `String` type.
350    pub constant_value: Option<ConstantValue>,
351}
352
353// ---------------------------------------------------------------------------
354// Annotation types
355// ---------------------------------------------------------------------------
356
357/// A single annotation instance (runtime-visible or runtime-invisible).
358#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct AnnotationStub {
360    /// Annotation type FQN (e.g.,
361    /// `"org.springframework.web.bind.annotation.RequestMapping"`).
362    pub type_fqn: String,
363    /// Annotation element-value pairs.
364    pub elements: Vec<AnnotationElement>,
365    /// Whether this annotation is retained at runtime
366    /// (`RuntimeVisibleAnnotations` vs `RuntimeInvisibleAnnotations`).
367    pub is_runtime_visible: bool,
368}
369
370/// A single element (key-value pair) within an annotation.
371#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct AnnotationElement {
373    /// Element name (e.g., `"value"`, `"method"`).
374    pub name: String,
375    /// Element value.
376    pub value: AnnotationElementValue,
377}
378
379/// The value of an annotation element (JVMS 4.7.16.1).
380#[derive(Debug, Clone, Serialize, Deserialize)]
381pub enum AnnotationElementValue {
382    /// Primitive or `String` constant.
383    Const(ConstantValue),
384    /// Enum constant reference.
385    EnumConst {
386        /// Fully qualified name of the enum type.
387        type_fqn: String,
388        /// Name of the enum constant.
389        const_name: String,
390    },
391    /// Class literal (e.g., `String.class`), stored as a type descriptor.
392    ClassInfo(String),
393    /// Nested annotation value.
394    Annotation(Box<AnnotationStub>),
395    /// Array of element values.
396    Array(Vec<AnnotationElementValue>),
397}
398
399// ---------------------------------------------------------------------------
400// Generic signature types (JVMS 4.7.9)
401// ---------------------------------------------------------------------------
402
403/// Class-level generic signature.
404///
405/// Encodes type parameters and parameterized super-types that are erased in
406/// the regular class descriptor. Parsed from the `Signature` attribute.
407#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct GenericClassSignature {
409    /// Type parameters (e.g., `[K, V]` in `HashMap<K, V>`).
410    pub type_parameters: Vec<TypeParameterStub>,
411    /// Superclass type (with type arguments applied).
412    pub superclass: TypeSignature,
413    /// Implemented interfaces (with type arguments applied).
414    pub interfaces: Vec<TypeSignature>,
415}
416
417/// Method-level generic signature.
418///
419/// Encodes type parameters, parameterized parameter/return types, and
420/// declared exception types.
421#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct GenericMethodSignature {
423    /// Type parameters declared on the method.
424    pub type_parameters: Vec<TypeParameterStub>,
425    /// Parameter types (with generics).
426    pub parameter_types: Vec<TypeSignature>,
427    /// Return type (with generics).
428    pub return_type: TypeSignature,
429    /// Declared exception types (from `throws` clause).
430    pub exception_types: Vec<TypeSignature>,
431}
432
433/// A formal type parameter declaration (e.g., `T extends Comparable<T>`).
434#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct TypeParameterStub {
436    /// Parameter name (e.g., `"T"`, `"K"`, `"V"`).
437    pub name: String,
438    /// Class bound (the first bound, which may be a class type). `None` if
439    /// the bound is implicitly `Object`.
440    pub class_bound: Option<TypeSignature>,
441    /// Additional interface bounds (e.g., `T extends Serializable & Comparable<T>`
442    /// — `Comparable<T>` would be an interface bound).
443    pub interface_bounds: Vec<TypeSignature>,
444}
445
446/// JVM type signature with full generic information.
447///
448/// This is the central type representation used throughout the stub model.
449/// It captures the full parameterized type structure that is erased in
450/// standard JVM descriptors.
451#[derive(Debug, Clone, Serialize, Deserialize)]
452pub enum TypeSignature {
453    /// Primitive type (byte, char, double, float, int, long, short, boolean)
454    /// or `void`.
455    Base(BaseType),
456    /// Class or interface type with optional type arguments.
457    Class {
458        /// Fully qualified name (e.g., `"java.util.Map"`).
459        fqn: String,
460        /// Type arguments (e.g., `[String, List<Integer>]`). Empty if the
461        /// type is used in its raw form.
462        type_arguments: Vec<TypeArgument>,
463    },
464    /// Type variable reference (e.g., `T` in a generic method body).
465    TypeVariable(String),
466    /// Array type (e.g., `String[]` → `Array(Class { fqn: "java.lang.String" })`).
467    Array(Box<TypeSignature>),
468    /// Wildcard type (used only inside type argument positions).
469    Wildcard {
470        /// Optional bound. `None` represents `?` (unbounded).
471        bound: Option<WildcardBound>,
472    },
473}
474
475/// JVM primitive types and `void`.
476#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
477pub enum BaseType {
478    /// `byte` (`B`)
479    Byte,
480    /// `char` (`C`)
481    Char,
482    /// `double` (`D`)
483    Double,
484    /// `float` (`F`)
485    Float,
486    /// `int` (`I`)
487    Int,
488    /// `long` (`J`)
489    Long,
490    /// `short` (`S`)
491    Short,
492    /// `boolean` (`Z`)
493    Boolean,
494    /// `void` (`V`)
495    Void,
496}
497
498/// A type argument in a parameterized type (JVMS 4.7.9.1).
499#[derive(Debug, Clone, Serialize, Deserialize)]
500pub enum TypeArgument {
501    /// Concrete type (e.g., `String` in `List<String>`).
502    Type(TypeSignature),
503    /// Upper-bounded wildcard (e.g., `? extends Number`).
504    Extends(TypeSignature),
505    /// Lower-bounded wildcard (e.g., `? super Integer`).
506    Super(TypeSignature),
507    /// Unbounded wildcard (`?`).
508    Unbounded,
509}
510
511/// Wildcard bound direction.
512#[derive(Debug, Clone, Serialize, Deserialize)]
513pub enum WildcardBound {
514    /// Upper bound (`? extends T`).
515    Extends(Box<TypeSignature>),
516    /// Lower bound (`? super T`).
517    Super(Box<TypeSignature>),
518}
519
520// ---------------------------------------------------------------------------
521// Lambda / method reference targets
522// ---------------------------------------------------------------------------
523
524/// A lambda or method-reference target extracted from the `BootstrapMethods`
525/// attribute (specifically `LambdaMetafactory` entries).
526#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct LambdaTargetStub {
528    /// Target method owner class FQN.
529    pub owner_fqn: String,
530    /// Target method name.
531    pub method_name: String,
532    /// Target method descriptor.
533    pub method_descriptor: String,
534    /// Bootstrap method handle kind (how the target is invoked).
535    pub reference_kind: ReferenceKind,
536}
537
538/// Method handle reference kinds (JVMS 5.4.3.5, Table 5.4.3.5-A).
539#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
540pub enum ReferenceKind {
541    /// `REF_getField` (1)
542    GetField,
543    /// `REF_getStatic` (2)
544    GetStatic,
545    /// `REF_putField` (3)
546    PutField,
547    /// `REF_putStatic` (4)
548    PutStatic,
549    /// `REF_invokeVirtual` (5)
550    InvokeVirtual,
551    /// `REF_invokeStatic` (6)
552    InvokeStatic,
553    /// `REF_invokeSpecial` (7)
554    InvokeSpecial,
555    /// `REF_newInvokeSpecial` (8)
556    NewInvokeSpecial,
557    /// `REF_invokeInterface` (9)
558    InvokeInterface,
559}
560
561// ---------------------------------------------------------------------------
562// Module info (Java 9+)
563// ---------------------------------------------------------------------------
564
565/// Module descriptor parsed from `module-info.class` (JVMS 4.7.25).
566#[derive(Debug, Clone, Serialize, Deserialize)]
567pub struct ModuleStub {
568    /// Module name (e.g., `"java.base"`).
569    pub name: String,
570    /// Module access flags.
571    pub access: AccessFlags,
572    /// Module version string (from `ModuleMainClass` or `ModulePackages`).
573    pub version: Option<String>,
574    /// `requires` directives.
575    pub requires: Vec<ModuleRequires>,
576    /// `exports` directives.
577    pub exports: Vec<ModuleExports>,
578    /// `opens` directives.
579    pub opens: Vec<ModuleOpens>,
580    /// `provides` directives.
581    pub provides: Vec<ModuleProvides>,
582    /// `uses` directives (service interfaces consumed).
583    pub uses: Vec<String>,
584}
585
586/// A `requires` directive in a module descriptor.
587#[derive(Debug, Clone, Serialize, Deserialize)]
588pub struct ModuleRequires {
589    /// Required module name.
590    pub module_name: String,
591    /// Flags (e.g., `ACC_TRANSITIVE`, `ACC_STATIC_PHASE`).
592    pub access: AccessFlags,
593    /// Required module version.
594    pub version: Option<String>,
595}
596
597/// An `exports` directive in a module descriptor.
598#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct ModuleExports {
600    /// Exported package name.
601    pub package: String,
602    /// Export flags.
603    pub access: AccessFlags,
604    /// Qualified exports target modules (empty = unqualified export).
605    pub to_modules: Vec<String>,
606}
607
608/// An `opens` directive in a module descriptor.
609#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct ModuleOpens {
611    /// Opened package name.
612    pub package: String,
613    /// Opens flags.
614    pub access: AccessFlags,
615    /// Qualified opens target modules (empty = unqualified).
616    pub to_modules: Vec<String>,
617}
618
619/// A `provides` directive in a module descriptor.
620#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct ModuleProvides {
622    /// Service interface FQN.
623    pub service: String,
624    /// Implementation class FQNs.
625    pub implementations: Vec<String>,
626}
627
628// ---------------------------------------------------------------------------
629// Record components (Java 16+)
630// ---------------------------------------------------------------------------
631
632/// A record component (parameter of a `record` class).
633#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct RecordComponent {
635    /// Component name (also the accessor method name).
636    pub name: String,
637    /// JVM descriptor of the component type.
638    pub descriptor: String,
639    /// Generic signature (if the component uses a parameterized type).
640    pub generic_signature: Option<TypeSignature>,
641    /// Component-level annotations.
642    pub annotations: Vec<AnnotationStub>,
643}
644
645// ---------------------------------------------------------------------------
646// Inner classes
647// ---------------------------------------------------------------------------
648
649/// An entry from the `InnerClasses` attribute (JVMS 4.7.6).
650#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct InnerClassEntry {
652    /// Inner class FQN.
653    pub inner_fqn: String,
654    /// Outer class FQN (`None` for anonymous or local classes).
655    pub outer_fqn: Option<String>,
656    /// Simple name of the inner class (`None` for anonymous classes).
657    pub inner_name: Option<String>,
658    /// Inner class access flags.
659    pub access: AccessFlags,
660}
661
662// ---------------------------------------------------------------------------
663// Constant values
664// ---------------------------------------------------------------------------
665
666/// Constant value from the `ConstantValue` attribute (JVMS 4.7.2) or
667/// annotation element values.
668#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
669pub enum ConstantValue {
670    /// `int` (or smaller integral types: `byte`, `char`, `short`, `boolean`).
671    Int(i32),
672    /// `long`.
673    Long(i64),
674    /// `float`.
675    Float(OrderedFloat<f32>),
676    /// `double`.
677    Double(OrderedFloat<f64>),
678    /// `String` (`CONSTANT_String`).
679    String(String),
680}
681
682/// Wrapper around `f32` that provides `PartialEq` with total ordering
683/// (treating NaN as equal to NaN) for use in [`ConstantValue`].
684///
685/// This is needed because `f32`/`f64` do not implement `Eq`, but we need
686/// `PartialEq` for testing round-trip serialization.
687#[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// ---------------------------------------------------------------------------
719// Kotlin metadata
720// ---------------------------------------------------------------------------
721
722/// Raw Kotlin metadata extracted from the `@kotlin.Metadata` annotation.
723///
724/// This is a placeholder populated during bytecode parsing (U03) and
725/// interpreted by the Kotlin metadata decoder (U18).
726#[derive(Debug, Clone, Serialize, Deserialize)]
727pub struct KotlinMetadataStub {
728    /// Metadata kind:
729    /// - 1 = Class
730    /// - 2 = File facade
731    /// - 3 = Synthetic class
732    /// - 4 = Multi-file class facade
733    /// - 5 = Multi-file class part
734    pub kind: u32,
735    /// Metadata version (e.g., `[1, 9, 0]`).
736    pub metadata_version: Vec<u32>,
737    /// Raw protobuf data (`d1` array from the annotation).
738    pub data1: Vec<String>,
739    /// Raw string table (`d2` array from the annotation).
740    pub data2: Vec<String>,
741    /// Extra string from the annotation (`xs`).
742    pub extra_string: Option<String>,
743    /// Package name from the annotation (`pn`).
744    pub package_name: Option<String>,
745    /// Extra int from the annotation (`xi`).
746    pub extra_int: Option<i32>,
747}
748
749// ---------------------------------------------------------------------------
750// Scala signature
751// ---------------------------------------------------------------------------
752
753/// Raw Scala signature extracted from the `@ScalaSignature` annotation.
754///
755/// This is a placeholder populated during bytecode parsing (U03) and
756/// interpreted by the Scala signature decoder (U19).
757#[derive(Debug, Clone, Serialize, Deserialize)]
758pub struct ScalaSignatureStub {
759    /// Raw signature bytes (decoded from base64 `bytes` element).
760    pub bytes: Vec<u8>,
761    /// Major version of the signature format.
762    pub major_version: u32,
763    /// Minor version of the signature format.
764    pub minor_version: u32,
765}
766
767// ---------------------------------------------------------------------------
768// Tests
769// ---------------------------------------------------------------------------
770
771#[cfg(test)]
772mod tests {
773    use super::*;
774
775    // -- Helper builders for test data --------------------------------------
776
777    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    // -- postcard round-trip helpers ----------------------------------------
848
849    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    // -- AccessFlags tests --------------------------------------------------
855
856    #[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    // -- ClassKind tests ----------------------------------------------------
966
967    #[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    // -- ClassStub round-trip -----------------------------------------------
983
984    #[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    // -- MethodStub round-trip ----------------------------------------------
1000
1001    #[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    // -- FieldStub round-trip -----------------------------------------------
1012
1013    #[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    // -- ConstantValue round-trip -------------------------------------------
1024
1025    #[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    // -- Annotation round-trip (simple) -------------------------------------
1056
1057    #[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    // -- Annotation with nested annotation ----------------------------------
1066
1067    #[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    // -- Annotation with array and enum values ------------------------------
1106
1107    #[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    // -- Generic signature round-trip (complex nested type args) ------------
1147
1148    #[test]
1149    fn generic_class_signature_round_trip() {
1150        // Represents: class MyMap<K extends Comparable<K>, V> extends HashMap<K, List<V>>
1151        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        // Represents: <T extends Number> List<T> convert(Collection<? extends T> input)
1196        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    // -- TypeSignature variants round-trip ----------------------------------
1230
1231    #[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        // String[][]
1258        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    // -- TypeArgument variants round-trip -----------------------------------
1302
1303    #[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    // -- ReferenceKind round-trip -------------------------------------------
1322
1323    #[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    // -- LambdaTargetStub round-trip ----------------------------------------
1342
1343    #[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    // -- ModuleStub round-trip ----------------------------------------------
1360
1361    #[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    // -- RecordComponent round-trip -----------------------------------------
1398
1399    #[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    // -- InnerClassEntry round-trip -----------------------------------------
1418
1419    #[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    // -- KotlinMetadataStub round-trip --------------------------------------
1449
1450    #[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    // -- ScalaSignatureStub round-trip --------------------------------------
1472
1473    #[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    // -- Full ClassStub with all optional fields ----------------------------
1487
1488    #[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    // -- ClassStub with module info -----------------------------------------
1579
1580    #[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    // -- ClassStub with enum constants --------------------------------------
1636
1637    #[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    // -- Send + Sync static assertions --------------------------------------
1672
1673    #[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}