Skip to main content

sim_codec_classfile/attribute/
annotations.rs

1/// Maximum annotation nesting accepted even when a caller supplies a larger budget.
2///
3/// This implementation ceiling keeps recursive parsing below the host stack's danger zone.
4pub const MAX_ANNOTATION_NESTING: usize = 256;
5
6/// The absolute half-open byte range occupied by one annotation structure.
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct AttributeOrigin {
9    /// Absolute offset of the structure's first byte.
10    pub start: usize,
11    /// Absolute offset immediately after the structure's last byte.
12    pub end: usize,
13}
14fn annotation_origin(start: usize, reader: &ByteReader<'_>) -> AttributeOrigin {
15    AttributeOrigin {
16        start,
17        end: reader.offset(),
18    }
19}
20
21/// One annotation element-name/value pair, retained in classfile order.
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct AnnotationElement {
24    /// Constant-pool index of the element name. Duplicate indices remain distinct entries.
25    pub name_index: u16,
26    /// Encoded element value.
27    pub value: ElementValue,
28    /// Source range of the complete pair.
29    pub origin: AttributeOrigin,
30}
31
32/// One annotation, retaining its unresolved type index, element order, and source range.
33#[derive(Clone, Debug, Eq, PartialEq)]
34pub struct Annotation {
35    /// Constant-pool index of the annotation interface descriptor.
36    pub type_index: u16,
37    /// Element pairs in encoded order, including duplicate names.
38    pub elements: Vec<AnnotationElement>,
39    /// Source range of the complete annotation.
40    pub origin: AttributeOrigin,
41}
42
43/// A JVM annotation `element_value`, with every constant-pool index left unresolved.
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub enum ElementValue {
46    /// Primitive or string constant (`B`, `C`, `D`, `F`, `I`, `J`, `S`, `Z`, or `s`).
47    Constant {
48        /// Exact element-value tag.
49        tag: u8,
50        /// Constant-pool index of the value.
51        constant_index: u16,
52        /// Source range of the complete value.
53        origin: AttributeOrigin,
54    },
55    /// Enum constant (`e`).
56    Enum {
57        /// Constant-pool index of the enum type name.
58        type_name_index: u16,
59        /// Constant-pool index of the enum constant name.
60        constant_name_index: u16,
61        /// Source range of the complete value.
62        origin: AttributeOrigin,
63    },
64    /// Class literal (`c`).
65    Class {
66        /// Constant-pool index of the return descriptor.
67        class_info_index: u16,
68        /// Source range of the complete value.
69        origin: AttributeOrigin,
70    },
71    /// Nested annotation (`@`).
72    Annotation {
73        /// Nested annotation value.
74        annotation: Box<Annotation>,
75        /// Source range including the tag.
76        origin: AttributeOrigin,
77    },
78    /// Ordered annotation array (`[`).
79    Array {
80        /// Values in encoded order.
81        values: Vec<ElementValue>,
82        /// Source range of the complete array.
83        origin: AttributeOrigin,
84    },
85}
86
87impl ElementValue {
88    /// Source range occupied by this value.
89    pub fn origin(&self) -> AttributeOrigin {
90        match self {
91            Self::Constant { origin, .. }
92            | Self::Enum { origin, .. }
93            | Self::Class { origin, .. }
94            | Self::Annotation { origin, .. }
95            | Self::Array { origin, .. } => *origin,
96        }
97    }
98}
99
100/// An ordered `RuntimeVisibleAnnotations` or `RuntimeInvisibleAnnotations` payload.
101#[derive(Clone, Debug, Eq, PartialEq)]
102pub struct AnnotationsAttribute {
103    /// Annotations in declaration order.
104    pub annotations: Vec<Annotation>,
105}
106
107impl AnnotationsAttribute {
108    /// Decode a declaration-annotation payload under the supplied nesting budget.
109    pub fn decode(
110        reader: &mut ByteReader<'_>,
111        nesting_budget: usize,
112    ) -> Result<Self, AttributeError> {
113        let annotations = decode_annotations(reader, nesting_budget)?;
114        finish(reader)?;
115        Ok(Self { annotations })
116    }
117
118    /// Encode without resolving or normalizing annotation indices.
119    pub fn encode(&self, budget: usize) -> Result<Vec<u8>, AttributeError> {
120        let mut out = ByteWriter::new(budget);
121        encode_annotations(&self.annotations, &mut out)?;
122        Ok(out.into_bytes())
123    }
124}
125
126/// An ordered `RuntimeVisibleParameterAnnotations` or
127/// `RuntimeInvisibleParameterAnnotations` payload.
128#[derive(Clone, Debug, Eq, PartialEq)]
129pub struct ParameterAnnotationsAttribute {
130    /// Per-parameter annotation lists in descriptor parameter order.
131    pub parameters: Vec<Vec<Annotation>>,
132}
133
134impl ParameterAnnotationsAttribute {
135    /// Decode a parameter-annotation payload under the supplied nesting budget.
136    pub fn decode(
137        reader: &mut ByteReader<'_>,
138        nesting_budget: usize,
139    ) -> Result<Self, AttributeError> {
140        let count = usize::from(reader.read_u1()?);
141        reader.preflight_allocation(count)?;
142        let mut parameters = Vec::with_capacity(count);
143        for _ in 0..count {
144            parameters.push(decode_annotations(reader, nesting_budget)?);
145        }
146        finish(reader)?;
147        Ok(Self { parameters })
148    }
149
150    /// Encode parameter and annotation order exactly as stored.
151    pub fn encode(&self, budget: usize) -> Result<Vec<u8>, AttributeError> {
152        let mut out = ByteWriter::new(budget);
153        out.write_u1(u8::try_from(self.parameters.len()).map_err(|_| {
154            error(
155                AttributeErrorKind::CountOverflow,
156                0,
157                "too many annotated parameters",
158            )
159        })?)?;
160        for annotations in &self.parameters {
161            encode_annotations(annotations, &mut out)?;
162        }
163        Ok(out.into_bytes())
164    }
165}
166
167/// One step in a type annotation's path from the annotated type to its target component.
168#[derive(Clone, Copy, Debug, Eq, PartialEq)]
169pub struct TypePathEntry {
170    /// Path kind (array, nested, wildcard bound, or type argument).
171    pub kind: u8,
172    /// Type-argument index; zero for kinds other than type argument.
173    pub argument_index: u8,
174    /// Source range of this path entry.
175    pub origin: AttributeOrigin,
176}
177
178/// The complete target-specific union from JVMS `target_info`.
179#[derive(Clone, Debug, Eq, PartialEq)]
180pub enum TypeAnnotationTarget {
181    /// Class or method type parameter.
182    TypeParameter {
183        /// Type parameter index.
184        index: u8,
185    },
186    /// Supertype, including `65535` for the superclass.
187    Supertype {
188        /// Supertype-table index.
189        index: u16,
190    },
191    /// Bound of a class or method type parameter.
192    TypeParameterBound {
193        /// Type parameter index.
194        parameter_index: u8,
195        /// Bound index within the parameter.
196        bound_index: u8,
197    },
198    /// Field, return type, or receiver target, whose union member is empty.
199    Empty,
200    /// Formal method parameter.
201    FormalParameter {
202        /// Formal parameter index.
203        index: u8,
204    },
205    /// `throws` clause entry.
206    Throws {
207        /// Throws-table index.
208        index: u16,
209    },
210    /// Local-variable table target, preserving table order and overlaps.
211    LocalVariable {
212        /// Ordered local-variable target table.
213        table: Vec<LocalVariableTarget>,
214    },
215    /// Exception-table entry.
216    Catch {
217        /// Exception-table index.
218        exception_table_index: u16,
219    },
220    /// `instanceof`, `new`, method reference, or constructor reference instruction.
221    Offset {
222        /// Instruction offset in the code array.
223        offset: u16,
224    },
225    /// Cast/invocation/reference type argument.
226    TypeArgument {
227        /// Instruction offset in the code array.
228        offset: u16,
229        /// Type-argument index at the instruction.
230        argument_index: u8,
231    },
232}
233
234/// One local-variable target range.
235#[derive(Clone, Copy, Debug, Eq, PartialEq)]
236pub struct LocalVariableTarget {
237    /// Inclusive code-array start offset.
238    pub start_pc: u16,
239    /// Range length in the code array.
240    pub length: u16,
241    /// Local-variable slot index.
242    pub index: u16,
243    /// Source range of this table row.
244    pub origin: AttributeOrigin,
245}
246
247/// One type annotation, including its target union and component path.
248#[derive(Clone, Debug, Eq, PartialEq)]
249pub struct TypeAnnotation {
250    /// Exact target-type discriminator.
251    pub target_type: u8,
252    /// Target data selected by `target_type`.
253    pub target: TypeAnnotationTarget,
254    /// Component path in encoded order.
255    pub path: Vec<TypePathEntry>,
256    /// Annotation type index.
257    pub type_index: u16,
258    /// Ordered annotation element pairs.
259    pub elements: Vec<AnnotationElement>,
260    /// Source range of the complete type annotation.
261    pub origin: AttributeOrigin,
262}
263
264/// An ordered runtime-visible or runtime-invisible type-annotation payload.
265#[derive(Clone, Debug, Eq, PartialEq)]
266pub struct TypeAnnotationsAttribute {
267    /// Type annotations in encoded order.
268    pub annotations: Vec<TypeAnnotation>,
269}
270
271impl TypeAnnotationsAttribute {
272    /// Decode all target unions and paths under the supplied nesting budget.
273    pub fn decode(
274        reader: &mut ByteReader<'_>,
275        nesting_budget: usize,
276    ) -> Result<Self, AttributeError> {
277        let n = usize::from(reader.read_u2()?);
278        reader.preflight_allocation(n)?;
279        let mut annotations = Vec::with_capacity(n);
280        for _ in 0..n {
281            annotations.push(decode_type_annotation(reader, nesting_budget)?);
282        }
283        finish(reader)?;
284        Ok(Self { annotations })
285    }
286
287    /// Encode target unions, paths, and elements exactly as stored.
288    pub fn encode(&self, budget: usize) -> Result<Vec<u8>, AttributeError> {
289        let mut out = ByteWriter::new(budget);
290        out.write_u2(count(self.annotations.len(), "type annotations")?)?;
291        for annotation in &self.annotations {
292            encode_type_annotation(annotation, &mut out)?;
293        }
294        Ok(out.into_bytes())
295    }
296}
297
298/// An `AnnotationDefault` payload.
299#[derive(Clone, Debug, Eq, PartialEq)]
300pub struct AnnotationDefaultAttribute {
301    /// The method's default element value.
302    pub value: ElementValue,
303}
304
305impl AnnotationDefaultAttribute {
306    /// Decode the default value under the supplied nesting budget.
307    pub fn decode(
308        reader: &mut ByteReader<'_>,
309        nesting_budget: usize,
310    ) -> Result<Self, AttributeError> {
311        let mut budget = NestingBudget::new(nesting_budget);
312        let value = decode_element_value(reader, &mut budget)?;
313        finish(reader)?;
314        Ok(Self { value })
315    }
316
317    /// Encode the default value exactly as stored.
318    pub fn encode(&self, budget: usize) -> Result<Vec<u8>, AttributeError> {
319        let mut out = ByteWriter::new(budget);
320        encode_element_value(&self.value, &mut out)?;
321        Ok(out.into_bytes())
322    }
323}
324
325struct NestingBudget {
326    remaining: usize,
327}
328
329impl NestingBudget {
330    fn new(requested: usize) -> Self {
331        Self {
332            remaining: requested.min(MAX_ANNOTATION_NESTING),
333        }
334    }
335
336    fn enter(&mut self, offset: usize) -> Result<(), AttributeError> {
337        self.remaining = self.remaining.checked_sub(1).ok_or_else(|| {
338            error(
339                AttributeErrorKind::NestingBudgetExceeded,
340                offset,
341                "annotation nesting budget exceeded",
342            )
343        })?;
344        Ok(())
345    }
346
347    fn leave(&mut self) {
348        self.remaining += 1;
349    }
350}
351
352fn decode_annotations(
353    reader: &mut ByteReader<'_>,
354    nesting_budget: usize,
355) -> Result<Vec<Annotation>, AttributeError> {
356    let n = usize::from(reader.read_u2()?);
357    reader.preflight_allocation(n)?;
358    let mut budget = NestingBudget::new(nesting_budget);
359    let mut annotations = Vec::with_capacity(n);
360    for _ in 0..n {
361        annotations.push(decode_annotation(reader, &mut budget)?);
362    }
363    Ok(annotations)
364}
365
366fn decode_annotation(
367    reader: &mut ByteReader<'_>,
368    budget: &mut NestingBudget,
369) -> Result<Annotation, AttributeError> {
370    let start = reader.offset();
371    let type_index = reader.read_u2()?;
372    let n = usize::from(reader.read_u2()?);
373    reader.preflight_allocation(n)?;
374    let mut elements = Vec::with_capacity(n);
375    for _ in 0..n {
376        let pair_start = reader.offset();
377        let name_index = reader.read_u2()?;
378        let value = decode_element_value(reader, budget)?;
379        elements.push(AnnotationElement {
380            name_index,
381            value,
382            origin: annotation_origin(pair_start, reader),
383        });
384    }
385    Ok(Annotation {
386        type_index,
387        elements,
388        origin: annotation_origin(start, reader),
389    })
390}
391
392fn decode_element_value(
393    reader: &mut ByteReader<'_>,
394    budget: &mut NestingBudget,
395) -> Result<ElementValue, AttributeError> {
396    let start = reader.offset();
397    let tag = reader.read_u1()?;
398    let value = match tag {
399        b'B' | b'C' | b'D' | b'F' | b'I' | b'J' | b'S' | b'Z' | b's' => ElementValue::Constant {
400            tag,
401            constant_index: reader.read_u2()?,
402            origin: annotation_origin(start, reader),
403        },
404        b'e' => ElementValue::Enum {
405            type_name_index: reader.read_u2()?,
406            constant_name_index: reader.read_u2()?,
407            origin: annotation_origin(start, reader),
408        },
409        b'c' => ElementValue::Class {
410            class_info_index: reader.read_u2()?,
411            origin: annotation_origin(start, reader),
412        },
413        b'@' => {
414            budget.enter(start)?;
415            let annotation = decode_annotation(reader, budget);
416            budget.leave();
417            ElementValue::Annotation {
418                annotation: Box::new(annotation?),
419                origin: annotation_origin(start, reader),
420            }
421        }
422        b'[' => {
423            budget.enter(start)?;
424            let n = usize::from(reader.read_u2()?);
425            // Depth is debited before this reservation, so rejected nested arrays never allocate.
426            reader.preflight_allocation(n)?;
427            let mut values = Vec::with_capacity(n);
428            let result: Result<Vec<ElementValue>, AttributeError> = (|| {
429                for _ in 0..n {
430                    values.push(decode_element_value(reader, budget)?);
431                }
432                Ok(values)
433            })();
434            budget.leave();
435            ElementValue::Array {
436                values: result?,
437                origin: annotation_origin(start, reader),
438            }
439        }
440        _ => {
441            return Err(error(
442                AttributeErrorKind::ReservedTag,
443                start,
444                format!("reserved annotation element tag {tag}"),
445            ));
446        }
447    };
448    Ok(value)
449}
450
451fn encode_annotations(values: &[Annotation], out: &mut ByteWriter) -> Result<(), AttributeError> {
452    out.write_u2(count(values.len(), "annotations")?)?;
453    for value in values {
454        encode_annotation(value, out)?;
455    }
456    Ok(())
457}
458
459fn encode_annotation(value: &Annotation, out: &mut ByteWriter) -> Result<(), AttributeError> {
460    out.write_u2(value.type_index)?;
461    out.write_u2(count(value.elements.len(), "annotation elements")?)?;
462    for element in &value.elements {
463        out.write_u2(element.name_index)?;
464        encode_element_value(&element.value, out)?;
465    }
466    Ok(())
467}
468
469fn encode_element_value(value: &ElementValue, out: &mut ByteWriter) -> Result<(), AttributeError> {
470    match value {
471        ElementValue::Constant {
472            tag,
473            constant_index,
474            ..
475        } => {
476            out.write_u1(*tag)?;
477            out.write_u2(*constant_index)?;
478        }
479        ElementValue::Enum {
480            type_name_index,
481            constant_name_index,
482            ..
483        } => {
484            out.write_u1(b'e')?;
485            out.write_u2(*type_name_index)?;
486            out.write_u2(*constant_name_index)?;
487        }
488        ElementValue::Class {
489            class_info_index, ..
490        } => {
491            out.write_u1(b'c')?;
492            out.write_u2(*class_info_index)?;
493        }
494        ElementValue::Annotation { annotation, .. } => {
495            out.write_u1(b'@')?;
496            encode_annotation(annotation, out)?;
497        }
498        ElementValue::Array { values, .. } => {
499            out.write_u1(b'[')?;
500            out.write_u2(count(values.len(), "annotation array values")?)?;
501            for item in values {
502                encode_element_value(item, out)?;
503            }
504        }
505    }
506    Ok(())
507}
508
509fn decode_type_annotation(
510    reader: &mut ByteReader<'_>,
511    nesting_budget: usize,
512) -> Result<TypeAnnotation, AttributeError> {
513    let start = reader.offset();
514    let target_type = reader.read_u1()?;
515    let target = match target_type {
516        0x00 | 0x01 => TypeAnnotationTarget::TypeParameter {
517            index: reader.read_u1()?,
518        },
519        0x10 => TypeAnnotationTarget::Supertype {
520            index: reader.read_u2()?,
521        },
522        0x11 | 0x12 => TypeAnnotationTarget::TypeParameterBound {
523            parameter_index: reader.read_u1()?,
524            bound_index: reader.read_u1()?,
525        },
526        0x13..=0x15 => TypeAnnotationTarget::Empty,
527        0x16 => TypeAnnotationTarget::FormalParameter {
528            index: reader.read_u1()?,
529        },
530        0x17 => TypeAnnotationTarget::Throws {
531            index: reader.read_u2()?,
532        },
533        0x40 | 0x41 => {
534            let n = usize::from(reader.read_u2()?);
535            reader.preflight_allocation(n)?;
536            let mut table = Vec::with_capacity(n);
537            for _ in 0..n {
538                let row_start = reader.offset();
539                table.push(LocalVariableTarget {
540                    start_pc: reader.read_u2()?,
541                    length: reader.read_u2()?,
542                    index: reader.read_u2()?,
543                    origin: annotation_origin(row_start, reader),
544                });
545            }
546            TypeAnnotationTarget::LocalVariable { table }
547        }
548        0x42 => TypeAnnotationTarget::Catch {
549            exception_table_index: reader.read_u2()?,
550        },
551        0x43..=0x46 => TypeAnnotationTarget::Offset {
552            offset: reader.read_u2()?,
553        },
554        0x47..=0x4b => TypeAnnotationTarget::TypeArgument {
555            offset: reader.read_u2()?,
556            argument_index: reader.read_u1()?,
557        },
558        _ => {
559            return Err(error(
560                AttributeErrorKind::ReservedTag,
561                start,
562                format!("reserved type annotation target {target_type:#04x}"),
563            ));
564        }
565    };
566    let path_len = usize::from(reader.read_u1()?);
567    reader.preflight_allocation(path_len)?;
568    let mut path = Vec::with_capacity(path_len);
569    for _ in 0..path_len {
570        let path_start = reader.offset();
571        let kind = reader.read_u1()?;
572        let argument_index = reader.read_u1()?;
573        if kind > 3 || (kind != 3 && argument_index != 0) {
574            return Err(error(
575                AttributeErrorKind::StaticConstraint,
576                path_start,
577                "invalid type annotation path entry",
578            ));
579        }
580        path.push(TypePathEntry {
581            kind,
582            argument_index,
583            origin: annotation_origin(path_start, reader),
584        });
585    }
586    let mut budget = NestingBudget::new(nesting_budget);
587    let annotation = decode_annotation(reader, &mut budget)?;
588    Ok(TypeAnnotation {
589        target_type,
590        target,
591        path,
592        type_index: annotation.type_index,
593        elements: annotation.elements,
594        origin: annotation_origin(start, reader),
595    })
596}
597
598fn encode_type_annotation(
599    value: &TypeAnnotation,
600    out: &mut ByteWriter,
601) -> Result<(), AttributeError> {
602    out.write_u1(value.target_type)?;
603    match (&value.target, value.target_type) {
604        (TypeAnnotationTarget::TypeParameter { index }, 0x00 | 0x01) => out.write_u1(*index)?,
605        (TypeAnnotationTarget::Supertype { index }, 0x10) => out.write_u2(*index)?,
606        (
607            TypeAnnotationTarget::TypeParameterBound {
608                parameter_index,
609                bound_index,
610            },
611            0x11 | 0x12,
612        ) => {
613            out.write_u1(*parameter_index)?;
614            out.write_u1(*bound_index)?;
615        }
616        (TypeAnnotationTarget::Empty, 0x13..=0x15) => {}
617        (TypeAnnotationTarget::FormalParameter { index }, 0x16) => out.write_u1(*index)?,
618        (TypeAnnotationTarget::Throws { index }, 0x17) => out.write_u2(*index)?,
619        (TypeAnnotationTarget::LocalVariable { table }, 0x40 | 0x41) => {
620            out.write_u2(count(table.len(), "local-variable targets")?)?;
621            for row in table {
622                out.write_u2(row.start_pc)?;
623                out.write_u2(row.length)?;
624                out.write_u2(row.index)?;
625            }
626        }
627        (
628            TypeAnnotationTarget::Catch {
629                exception_table_index,
630            },
631            0x42,
632        ) => out.write_u2(*exception_table_index)?,
633        (TypeAnnotationTarget::Offset { offset }, 0x43..=0x46) => out.write_u2(*offset)?,
634        (
635            TypeAnnotationTarget::TypeArgument {
636                offset,
637                argument_index,
638            },
639            0x47..=0x4b,
640        ) => {
641            out.write_u2(*offset)?;
642            out.write_u1(*argument_index)?;
643        }
644        _ => {
645            return Err(error(
646                AttributeErrorKind::StaticConstraint,
647                0,
648                "type annotation target does not match target_type",
649            ));
650        }
651    }
652    out.write_u1(u8::try_from(value.path.len()).map_err(|_| {
653        error(
654            AttributeErrorKind::CountOverflow,
655            0,
656            "type path is too long",
657        )
658    })?)?;
659    for entry in &value.path {
660        if entry.kind > 3 || (entry.kind != 3 && entry.argument_index != 0) {
661            return Err(error(
662                AttributeErrorKind::StaticConstraint,
663                0,
664                "invalid type annotation path entry",
665            ));
666        }
667        out.write_u1(entry.kind)?;
668        out.write_u1(entry.argument_index)?;
669    }
670    encode_annotation(
671        &Annotation {
672            type_index: value.type_index,
673            elements: value.elements.clone(),
674            origin: value.origin,
675        },
676        out,
677    )
678}