Skip to main content

ferro_babe/model/
stack_map.rs

1use rust_asm::class_reader::{
2    StackMapFrame as RawStackMapFrame, VerificationTypeInfo as RawVerificationType,
3};
4use rust_asm::constant_pool::CpInfo;
5
6use super::ConstantPoolIndex;
7
8/// A borrowed view of one `StackMapTable` frame.
9///
10/// Frame offsets are encoded as deltas by the class-file format. Use
11/// [`Self::offset_delta`] together with preceding frames to recover an absolute
12/// bytecode offset.
13#[derive(Debug, Clone, Copy)]
14pub struct StackMapFrame<'a> {
15    inner: &'a RawStackMapFrame,
16    constant_pool: &'a [CpInfo],
17}
18
19impl<'a> StackMapFrame<'a> {
20    pub(crate) const fn new(inner: &'a RawStackMapFrame, constant_pool: &'a [CpInfo]) -> Self {
21        Self {
22            inner,
23            constant_pool,
24        }
25    }
26
27    /// Returns this frame's encoded offset delta.
28    #[must_use]
29    pub const fn offset_delta(&self) -> u16 {
30        match self.inner {
31            RawStackMapFrame::SameFrame { offset_delta }
32            | RawStackMapFrame::SameLocals1StackItemFrame { offset_delta, .. }
33            | RawStackMapFrame::SameLocals1StackItemFrameExtended { offset_delta, .. }
34            | RawStackMapFrame::ChopFrame { offset_delta, .. }
35            | RawStackMapFrame::SameFrameExtended { offset_delta }
36            | RawStackMapFrame::AppendFrame { offset_delta, .. }
37            | RawStackMapFrame::FullFrame { offset_delta, .. } => *offset_delta,
38        }
39    }
40
41    /// Returns the frame encoding kind.
42    #[must_use]
43    pub const fn kind(&self) -> StackMapFrameKind {
44        match self.inner {
45            RawStackMapFrame::SameFrame { .. } => StackMapFrameKind::Same,
46            RawStackMapFrame::SameLocals1StackItemFrame { .. }
47            | RawStackMapFrame::SameLocals1StackItemFrameExtended { .. } => {
48                StackMapFrameKind::SameLocalsOneStackItem
49            }
50            RawStackMapFrame::ChopFrame { k, .. } => StackMapFrameKind::Chop { count: *k },
51            RawStackMapFrame::SameFrameExtended { .. } => StackMapFrameKind::SameExtended,
52            RawStackMapFrame::AppendFrame { .. } => StackMapFrameKind::Append,
53            RawStackMapFrame::FullFrame { .. } => StackMapFrameKind::Full,
54        }
55    }
56
57    /// Returns the local entries encoded by an append or full frame.
58    ///
59    /// Returns `None` for same and chop frames, whose locals are inherited from
60    /// the preceding frame.
61    #[must_use]
62    pub fn locals(&self) -> Option<VerificationTypes<'a>> {
63        let values = match self.inner {
64            RawStackMapFrame::AppendFrame { locals, .. }
65            | RawStackMapFrame::FullFrame { locals, .. } => locals,
66            _ => return None,
67        };
68        Some(VerificationTypes::new(values, self.constant_pool))
69    }
70
71    /// Returns the stack entries encoded by this frame.
72    ///
73    /// Same and chop frames have an empty operand stack, so they return `None`.
74    #[must_use]
75    pub fn stack(&self) -> Option<VerificationTypes<'a>> {
76        match self.inner {
77            RawStackMapFrame::SameLocals1StackItemFrame { stack, .. }
78            | RawStackMapFrame::SameLocals1StackItemFrameExtended { stack, .. } => {
79                Some(VerificationTypes::single(stack, self.constant_pool))
80            }
81            RawStackMapFrame::FullFrame { stack, .. } => {
82                Some(VerificationTypes::new(stack, self.constant_pool))
83            }
84            _ => None,
85        }
86    }
87}
88
89/// Encoding form of a [`StackMapFrame`].
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum StackMapFrameKind {
92    /// A compact frame with inherited locals and an empty operand stack.
93    Same,
94    /// A frame with inherited locals and one operand-stack entry.
95    SameLocalsOneStackItem,
96    /// A frame that removes local entries from the preceding frame.
97    Chop {
98        /// Number of local entries removed from the preceding frame.
99        count: u8,
100    },
101    /// An extended same frame with a wider offset delta.
102    SameExtended,
103    /// A frame that appends local entries to the preceding frame.
104    Append,
105    /// A frame that supplies complete local and operand-stack entries.
106    Full,
107}
108
109/// A borrowed sequence of verification types in a stack-map frame.
110#[derive(Debug, Clone, Copy)]
111pub struct VerificationTypes<'a> {
112    values: &'a [RawVerificationType],
113    constant_pool: &'a [CpInfo],
114}
115
116impl<'a> VerificationTypes<'a> {
117    const fn new(values: &'a [RawVerificationType], constant_pool: &'a [CpInfo]) -> Self {
118        Self {
119            values,
120            constant_pool,
121        }
122    }
123
124    const fn single(value: &'a RawVerificationType, constant_pool: &'a [CpInfo]) -> Self {
125        Self::new(std::slice::from_ref(value), constant_pool)
126    }
127
128    /// Returns the number of verification-type entries.
129    #[must_use]
130    pub const fn len(&self) -> usize {
131        self.values.len()
132    }
133
134    /// Returns whether this sequence has no entries.
135    #[must_use]
136    pub const fn is_empty(&self) -> bool {
137        self.values.is_empty()
138    }
139
140    /// Iterates over verification types in class-file order.
141    pub fn iter(&self) -> impl ExactSizeIterator<Item = VerificationType<'a>> + '_ {
142        self.values
143            .iter()
144            .map(|value| VerificationType::from_raw(value, self.constant_pool))
145    }
146}
147
148/// A verification type stored in a `StackMapTable` frame.
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum VerificationType<'a> {
151    /// An unusable local-variable slot.
152    Top,
153    /// A category-one integral value.
154    Integer,
155    /// A category-one floating-point value.
156    Float,
157    /// A category-two integral value.
158    Long,
159    /// A category-two floating-point value.
160    Double,
161    /// The JVM `null` value.
162    Null,
163    /// The receiver in an instance constructor before initialization completes.
164    UninitializedThis,
165    /// A reference to a class or array type.
166    Object {
167        /// Resolved internal class name or array descriptor, when available.
168        internal_name: Option<&'a str>,
169        /// Raw constant-pool index of the class entry.
170        constant_pool_index: ConstantPoolIndex,
171    },
172    /// An object allocated by `new` before constructor initialization completes.
173    Uninitialized {
174        /// Bytecode offset of the allocating `new` instruction.
175        offset: u16,
176    },
177}
178
179impl<'a> VerificationType<'a> {
180    fn from_raw(value: &'a RawVerificationType, constant_pool: &'a [CpInfo]) -> Self {
181        match value {
182            RawVerificationType::Top => Self::Top,
183            RawVerificationType::Integer => Self::Integer,
184            RawVerificationType::Float => Self::Float,
185            RawVerificationType::Long => Self::Long,
186            RawVerificationType::Double => Self::Double,
187            RawVerificationType::Null => Self::Null,
188            RawVerificationType::UninitializedThis => Self::UninitializedThis,
189            RawVerificationType::Object { cpool_index } => Self::Object {
190                internal_name: class_name(constant_pool, *cpool_index),
191                constant_pool_index: ConstantPoolIndex::new(*cpool_index),
192            },
193            RawVerificationType::Uninitialized { offset } => {
194                Self::Uninitialized { offset: *offset }
195            }
196        }
197    }
198}
199
200fn class_name(constant_pool: &[CpInfo], index: u16) -> Option<&str> {
201    let CpInfo::Class { name_index } = constant_pool.get(usize::from(index))? else {
202        return None;
203    };
204    let CpInfo::Utf8(name) = constant_pool.get(usize::from(*name_index))? else {
205        return None;
206    };
207    Some(name)
208}