Skip to main content

sim_codec_classfile/attribute/
code.rs

1/// A nested attribute retained in declaration order with an unresolved name index.
2#[derive(Clone, Debug, Eq, PartialEq)]
3pub struct NestedAttribute {
4    /// Constant-pool index naming the attribute.
5    pub name_index: u16,
6    /// Owner category in which this attribute was decoded.
7    pub owner: NestedAttributeOwner,
8    /// Zero-based order within its owner's table.
9    pub order: usize,
10    /// Body length declared by the attribute header.
11    pub declared_length: u32,
12    /// Exact attribute payload.
13    pub bytes: Vec<u8>,
14    /// Source range covering the complete header and body.
15    pub origin: AttributeOrigin,
16}
17/// Legal owners for attributes nested inside structured attribute bodies.
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum NestedAttributeOwner {
20    /// A `Code` attribute.
21    Code,
22    /// A record component.
23    RecordComponent,
24}
25
26/// One `Code` exception-table row, with all offsets and the catch index retained verbatim.
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub struct CodeException {
29    /// Inclusive bytecode start offset.
30    pub start_pc: u16,
31    /// Exclusive bytecode end offset.
32    pub end_pc: u16,
33    /// Handler bytecode offset.
34    pub handler_pc: u16,
35    /// Constant-pool class index, or zero for a catch-all handler.
36    pub catch_type: u16,
37}
38
39/// The ordered, index-preserving body of a JVM `Code` attribute.
40#[derive(Clone, Debug, Eq, PartialEq)]
41pub struct CodeAttribute {
42    /// Declared operand-stack bound.
43    pub max_stack: u16,
44    /// Declared local-variable bound.
45    pub max_locals: u16,
46    /// Exact bytecode array; instruction decoding remains a separate operation.
47    pub code: Vec<u8>,
48    /// Exception handlers in classfile order.
49    pub exception_table: Vec<CodeException>,
50    /// Nested attributes in classfile order.
51    pub attributes: Vec<NestedAttribute>,
52}
53
54impl CodeAttribute {
55    /// Decode a complete `Code` payload, checking only its binary shape and allocation budget.
56    pub fn decode(reader: &mut ByteReader<'_>) -> Result<Self, AttributeError> {
57        let max_stack = reader.read_u2()?;
58        let max_locals = reader.read_u2()?;
59        let code_len = usize::try_from(reader.read_u4()?).map_err(|_| {
60            error(
61                AttributeErrorKind::CountOverflow,
62                reader.offset(),
63                "code length is not addressable",
64            )
65        })?;
66        reader.preflight_allocation(code_len)?;
67        let code = reader.take(code_len)?.to_vec();
68        let exception_count = usize::from(reader.read_u2()?);
69        reader.preflight_allocation(exception_count)?;
70        let mut exception_table = Vec::with_capacity(exception_count);
71        for _ in 0..exception_count {
72            exception_table.push(CodeException {
73                start_pc: reader.read_u2()?,
74                end_pc: reader.read_u2()?,
75                handler_pc: reader.read_u2()?,
76                catch_type: reader.read_u2()?,
77            });
78        }
79        validate_code_shape(code.len(), &exception_table)?;
80        let attribute_count = usize::from(reader.read_u2()?);
81        reader.preflight_allocation(attribute_count)?;
82        let mut attributes = Vec::with_capacity(attribute_count);
83        for order in 0..attribute_count {
84            let start = reader.offset();
85            let name_index = reader.read_u2()?;
86            let declared_length = reader.read_u4()?;
87            let length = usize::try_from(declared_length).map_err(|_| {
88                error(
89                    AttributeErrorKind::CountOverflow,
90                    reader.offset(),
91                    "nested attribute length is not addressable",
92                )
93            })?;
94            reader.preflight_allocation(length)?;
95            attributes.push(NestedAttribute {
96                name_index,
97                owner: NestedAttributeOwner::Code,
98                order,
99                declared_length,
100                bytes: reader.take(length)?.to_vec(),
101                origin: annotation_origin(start, reader),
102            });
103        }
104        finish(reader)?;
105        Ok(Self {
106            max_stack,
107            max_locals,
108            code,
109            exception_table,
110            attributes,
111        })
112    }
113
114    /// Encode the payload without resolving indices or performing bytecode verification.
115    pub fn encode(&self, budget: usize) -> Result<Vec<u8>, AttributeError> {
116        validate_code_shape(self.code.len(), &self.exception_table)?;
117        let mut out = ByteWriter::new(budget);
118        out.write_u2(self.max_stack)?;
119        out.write_u2(self.max_locals)?;
120        out.write_u4(
121            u32::try_from(self.code.len())
122                .map_err(|_| error(AttributeErrorKind::CountOverflow, 0, "code is too long"))?,
123        )?;
124        out.write_bytes(&self.code)?;
125        out.write_u2(count(self.exception_table.len(), "exception handlers")?)?;
126        for row in &self.exception_table {
127            out.write_u2(row.start_pc)?;
128            out.write_u2(row.end_pc)?;
129            out.write_u2(row.handler_pc)?;
130            out.write_u2(row.catch_type)?;
131        }
132        out.write_u2(count(self.attributes.len(), "nested attributes")?)?;
133        for attribute in &self.attributes {
134            if usize::try_from(attribute.declared_length).ok() != Some(attribute.bytes.len()) {
135                return Err(error(
136                    AttributeErrorKind::StaticConstraint,
137                    attribute.origin.start,
138                    "nested attribute declared length differs from retained bytes",
139                ));
140            }
141            out.write_u2(attribute.name_index)?;
142            out.write_u4(attribute.declared_length)?;
143            out.write_bytes(&attribute.bytes)?;
144        }
145        Ok(out.into_bytes())
146    }
147}
148
149fn validate_code_shape(
150    code_length: usize,
151    exceptions: &[CodeException],
152) -> Result<(), AttributeError> {
153    if !(1..=u16::MAX as usize).contains(&code_length) {
154        return Err(error(
155            AttributeErrorKind::StaticConstraint,
156            0,
157            format!("Code array length {code_length} is outside 1..=65535"),
158        ));
159    }
160    for exception in exceptions {
161        let start = usize::from(exception.start_pc);
162        let end = usize::from(exception.end_pc);
163        let handler = usize::from(exception.handler_pc);
164        if start >= end || end > code_length || handler >= code_length {
165            return Err(error(
166                AttributeErrorKind::StaticConstraint,
167                start,
168                format!(
169                    "exception range {start}..{end} with handler {handler} is outside Code length {code_length}"
170                ),
171            ));
172        }
173    }
174    Ok(())
175}
176
177/// A verifier type exactly as represented by `verification_type_info`.
178#[derive(Clone, Copy, Debug, Eq, PartialEq)]
179pub enum VerificationType {
180    /// `Top_variable_info`.
181    Top,
182    /// `Integer_variable_info`.
183    Integer,
184    /// `Float_variable_info`.
185    Float,
186    /// `Double_variable_info`.
187    Double,
188    /// `Long_variable_info`.
189    Long,
190    /// `Null_variable_info`.
191    Null,
192    /// `UninitializedThis_variable_info`.
193    UninitializedThis,
194    /// `Object_variable_info`, retaining its constant-pool index.
195    Object(u16),
196    /// `Uninitialized_variable_info`, retaining its `new` instruction offset verbatim.
197    Uninitialized(u16),
198}
199
200/// One compressed stack-map frame, retained without expansion.
201#[derive(Clone, Debug, Eq, PartialEq)]
202pub enum StackMapFrame {
203    /// Tags 0 through 63.
204    Same {
205        /// Encoded frame tag, which is also the offset delta.
206        frame_type: u8,
207    },
208    /// Tags 64 through 127.
209    SameLocalsOneStack {
210        /// Encoded frame tag.
211        frame_type: u8,
212        /// Sole stack entry.
213        stack: VerificationType,
214    },
215    /// Tag 247.
216    SameLocalsOneStackExtended {
217        /// Explicit offset delta.
218        offset_delta: u16,
219        /// Sole stack entry.
220        stack: VerificationType,
221    },
222    /// Tags 248 through 250.
223    Chop {
224        /// Encoded frame tag, retaining the exact number of omitted locals.
225        frame_type: u8,
226        /// Explicit offset delta.
227        offset_delta: u16,
228    },
229    /// Tag 251.
230    SameExtended {
231        /// Explicit offset delta.
232        offset_delta: u16,
233    },
234    /// Tags 252 through 254.
235    Append {
236        /// Encoded frame tag, retaining the exact number of appended locals.
237        frame_type: u8,
238        /// Explicit offset delta.
239        offset_delta: u16,
240        /// Appended locals in encoded order.
241        locals: Vec<VerificationType>,
242    },
243    /// Tag 255.
244    Full {
245        /// Explicit offset delta.
246        offset_delta: u16,
247        /// Complete locals in encoded order.
248        locals: Vec<VerificationType>,
249        /// Complete stack in encoded order.
250        stack: Vec<VerificationType>,
251    },
252}
253
254/// An ordered `StackMapTable` payload.
255#[derive(Clone, Debug, Eq, PartialEq)]
256pub struct StackMapTableAttribute {
257    /// Frames in their original compressed representation.
258    pub frames: Vec<StackMapFrame>,
259}
260
261impl StackMapTableAttribute {
262    /// Decode a complete stack-map payload without performing type-state verification.
263    pub fn decode(reader: &mut ByteReader<'_>) -> Result<Self, AttributeError> {
264        let n = usize::from(reader.read_u2()?);
265        reader.preflight_allocation(n)?;
266        let mut frames = Vec::with_capacity(n);
267        for _ in 0..n {
268            frames.push(decode_frame(reader)?);
269        }
270        finish(reader)?;
271        Ok(Self { frames })
272    }
273    /// Encode frames in their retained compressed forms.
274    pub fn encode(&self, budget: usize) -> Result<Vec<u8>, AttributeError> {
275        let mut out = ByteWriter::new(budget);
276        out.write_u2(count(self.frames.len(), "stack-map frames")?)?;
277        for frame in &self.frames {
278            encode_frame(frame, &mut out)?;
279        }
280        Ok(out.into_bytes())
281    }
282}
283
284fn decode_type(reader: &mut ByteReader<'_>) -> Result<VerificationType, AttributeError> {
285    let at = reader.offset();
286    Ok(match reader.read_u1()? {
287        0 => VerificationType::Top,
288        1 => VerificationType::Integer,
289        2 => VerificationType::Float,
290        3 => VerificationType::Double,
291        4 => VerificationType::Long,
292        5 => VerificationType::Null,
293        6 => VerificationType::UninitializedThis,
294        7 => VerificationType::Object(reader.read_u2()?),
295        8 => VerificationType::Uninitialized(reader.read_u2()?),
296        tag => {
297            return Err(error(
298                AttributeErrorKind::ReservedTag,
299                at,
300                format!("reserved verification type tag {tag}"),
301            ));
302        }
303    })
304}
305
306fn encode_type(value: VerificationType, out: &mut ByteWriter) -> Result<(), AttributeError> {
307    let (tag, extra) = match value {
308        VerificationType::Top => (0, None),
309        VerificationType::Integer => (1, None),
310        VerificationType::Float => (2, None),
311        VerificationType::Double => (3, None),
312        VerificationType::Long => (4, None),
313        VerificationType::Null => (5, None),
314        VerificationType::UninitializedThis => (6, None),
315        VerificationType::Object(v) => (7, Some(v)),
316        VerificationType::Uninitialized(v) => (8, Some(v)),
317    };
318    out.write_u1(tag)?;
319    if let Some(v) = extra {
320        out.write_u2(v)?;
321    }
322    Ok(())
323}
324
325fn decode_frame(r: &mut ByteReader<'_>) -> Result<StackMapFrame, AttributeError> {
326    let at = r.offset();
327    let tag = r.read_u1()?;
328    Ok(match tag {
329        0..=63 => StackMapFrame::Same { frame_type: tag },
330        64..=127 => StackMapFrame::SameLocalsOneStack {
331            frame_type: tag,
332            stack: decode_type(r)?,
333        },
334        128..=246 => {
335            return Err(error(
336                AttributeErrorKind::ReservedTag,
337                at,
338                format!("reserved stack-map frame tag {tag}"),
339            ));
340        }
341        247 => StackMapFrame::SameLocalsOneStackExtended {
342            offset_delta: r.read_u2()?,
343            stack: decode_type(r)?,
344        },
345        248..=250 => StackMapFrame::Chop {
346            frame_type: tag,
347            offset_delta: r.read_u2()?,
348        },
349        251 => StackMapFrame::SameExtended {
350            offset_delta: r.read_u2()?,
351        },
352        252..=254 => {
353            let offset_delta = r.read_u2()?;
354            let mut locals = Vec::with_capacity(usize::from(tag - 251));
355            for _ in 0..tag - 251 {
356                locals.push(decode_type(r)?);
357            }
358            StackMapFrame::Append {
359                frame_type: tag,
360                offset_delta,
361                locals,
362            }
363        }
364        255 => {
365            let offset_delta = r.read_u2()?;
366            let nl = usize::from(r.read_u2()?);
367            r.preflight_allocation(nl)?;
368            let mut locals = Vec::with_capacity(nl);
369            for _ in 0..nl {
370                locals.push(decode_type(r)?);
371            }
372            let ns = usize::from(r.read_u2()?);
373            r.preflight_allocation(ns)?;
374            let mut stack = Vec::with_capacity(ns);
375            for _ in 0..ns {
376                stack.push(decode_type(r)?);
377            }
378            StackMapFrame::Full {
379                offset_delta,
380                locals,
381                stack,
382            }
383        }
384    })
385}
386
387fn encode_frame(f: &StackMapFrame, out: &mut ByteWriter) -> Result<(), AttributeError> {
388    match f {
389        StackMapFrame::Same { frame_type: t } if *t <= 63 => out.write_u1(*t)?,
390        StackMapFrame::SameLocalsOneStack {
391            frame_type: t,
392            stack,
393        } if (64..=127).contains(t) => {
394            out.write_u1(*t)?;
395            encode_type(*stack, out)?
396        }
397        StackMapFrame::SameLocalsOneStackExtended {
398            offset_delta,
399            stack,
400        } => {
401            out.write_u1(247)?;
402            out.write_u2(*offset_delta)?;
403            encode_type(*stack, out)?
404        }
405        StackMapFrame::Chop {
406            frame_type: t,
407            offset_delta,
408        } if (248..=250).contains(t) => {
409            out.write_u1(*t)?;
410            out.write_u2(*offset_delta)?
411        }
412        StackMapFrame::SameExtended { offset_delta } => {
413            out.write_u1(251)?;
414            out.write_u2(*offset_delta)?
415        }
416        StackMapFrame::Append {
417            frame_type: t,
418            offset_delta,
419            locals,
420        } if (252..=254).contains(t) && locals.len() == usize::from(*t - 251) => {
421            out.write_u1(*t)?;
422            out.write_u2(*offset_delta)?;
423            for v in locals {
424                encode_type(*v, out)?
425            }
426        }
427        StackMapFrame::Full {
428            offset_delta,
429            locals,
430            stack,
431        } => {
432            out.write_u1(255)?;
433            out.write_u2(*offset_delta)?;
434            out.write_u2(count(locals.len(), "full-frame locals")?)?;
435            for v in locals {
436                encode_type(*v, out)?
437            }
438            out.write_u2(count(stack.len(), "full-frame stack entries")?)?;
439            for v in stack {
440                encode_type(*v, out)?
441            }
442        }
443        _ => {
444            return Err(error(
445                AttributeErrorKind::ReservedTag,
446                0,
447                "frame variant contains a tag or arity outside its static format",
448            ));
449        }
450    }
451    Ok(())
452}