sim_codec_classfile/constant/
model.rs1#[derive(Clone, Debug, PartialEq)]
3pub enum Constant {
4 Utf8(CodeUnitString),
6 Integer(u32),
8 Float(u32),
10 Long(u64),
12 Double(u64),
14 Class {
16 name_index: u16,
18 },
19 String {
21 string_index: u16,
23 },
24 Fieldref {
26 class_index: u16,
28 name_and_type_index: u16,
30 },
31 Methodref {
33 class_index: u16,
35 name_and_type_index: u16,
37 },
38 InterfaceMethodref {
40 class_index: u16,
42 name_and_type_index: u16,
44 },
45 NameAndType {
47 name_index: u16,
49 descriptor_index: u16,
51 },
52 MethodHandle {
54 reference_kind: u8,
56 reference_index: u16,
58 },
59 MethodType {
61 descriptor_index: u16,
63 },
64 Dynamic {
66 bootstrap_method_attr_index: u16,
68 name_and_type_index: u16,
70 },
71 InvokeDynamic {
73 bootstrap_method_attr_index: u16,
75 name_and_type_index: u16,
77 },
78 Module {
80 name_index: u16,
82 },
83 Package {
85 name_index: u16,
87 },
88}
89#[derive(Clone, Debug, PartialEq)]
91pub enum ConstantSlot {
92 Reserved,
94 Entry(Constant),
96 Unusable,
98}
99
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub enum ConstantPoolErrorKind {
103 Bytes,
105 UnknownTag,
107 Version,
109 ReferenceKind,
111 InvalidIndex,
113 UnusableTarget,
115 WrongCategory,
117 InvalidLayout,
119}
120
121#[derive(Clone, Debug, Eq, PartialEq)]
123pub struct ConstantPoolError {
124 pub kind: ConstantPoolErrorKind,
126 pub index: u16,
128 pub target_index: Option<u16>,
130 pub message: String,
132}
133
134impl ConstantPoolError {
135 fn new(
136 kind: ConstantPoolErrorKind,
137 index: u16,
138 target: Option<u16>,
139 message: impl Into<String>,
140 ) -> Self {
141 Self {
142 kind,
143 index,
144 target_index: target,
145 message: message.into(),
146 }
147 }
148
149 fn bytes(index: u16, error: ByteError) -> Self {
150 Self::new(ConstantPoolErrorKind::Bytes, index, None, error.to_string())
151 }
152}
153
154impl fmt::Display for ConstantPoolError {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 write!(f, "{} at constant-pool index {}", self.message, self.index)
157 }
158}
159
160impl std::error::Error for ConstantPoolError {}
161
162#[derive(Clone, Debug, PartialEq)]
164pub struct ConstantPool {
165 slots: Vec<ConstantSlot>,
166}
167
168impl ConstantPool {
169 pub fn decode(
171 reader: &mut ByteReader<'_>,
172 major_version: u16,
173 ) -> Result<Self, ConstantPoolError> {
174 let count = reader
175 .read_u2()
176 .map_err(|e| ConstantPoolError::bytes(0, e))?;
177 if count == 0 {
178 return Err(ConstantPoolError::new(
179 ConstantPoolErrorKind::InvalidLayout,
180 0,
181 None,
182 "constant_pool_count must include the reserved zero index",
183 ));
184 }
185 reader
186 .preflight_allocation(usize::from(count))
187 .map_err(|e| ConstantPoolError::bytes(0, e))?;
188 let mut slots = Vec::with_capacity(usize::from(count));
189 slots.push(ConstantSlot::Reserved);
190 let mut index = 1u16;
191 while index < count {
192 let tag = reader
193 .read_u1()
194 .map_err(|e| ConstantPoolError::bytes(index, e))?;
195 let constant = decode_constant(reader, index, tag, major_version)?;
196 let two_slot = matches!(constant, Constant::Long(_) | Constant::Double(_));
197 slots.push(ConstantSlot::Entry(constant));
198 index += 1;
199 if two_slot {
200 if index >= count {
201 return Err(ConstantPoolError::new(
202 ConstantPoolErrorKind::InvalidLayout,
203 index - 1,
204 None,
205 "two-slot constant has no trailing unusable index",
206 ));
207 }
208 slots.push(ConstantSlot::Unusable);
209 index += 1;
210 }
211 }
212 let pool = Self { slots };
213 pool.validate(major_version)?;
214 Ok(pool)
215 }
216
217 pub fn slots(&self) -> &[ConstantSlot] {
219 &self.slots
220 }
221
222 pub fn entry(
224 &self,
225 source_index: u16,
226 target_index: u16,
227 ) -> Result<&Constant, ConstantPoolError> {
228 match self.slots.get(usize::from(target_index)) {
229 Some(ConstantSlot::Entry(value)) => Ok(value),
230 Some(ConstantSlot::Reserved | ConstantSlot::Unusable) => Err(ConstantPoolError::new(
231 ConstantPoolErrorKind::UnusableTarget,
232 source_index,
233 Some(target_index),
234 format!("index {source_index} points at unusable index {target_index}"),
235 )),
236 None => Err(ConstantPoolError::new(
237 ConstantPoolErrorKind::InvalidIndex,
238 source_index,
239 Some(target_index),
240 format!("index {source_index} points outside the pool at index {target_index}"),
241 )),
242 }
243 }
244
245 pub fn validate(&self, major_version: u16) -> Result<(), ConstantPoolError> {
247 if !matches!(self.slots.first(), Some(ConstantSlot::Reserved))
248 || self.slots.len() > usize::from(u16::MAX)
249 {
250 return Err(ConstantPoolError::new(
251 ConstantPoolErrorKind::InvalidLayout,
252 0,
253 None,
254 "pool must begin with exactly one reserved slot and fit u16",
255 ));
256 }
257 for (position, slot) in self.slots.iter().enumerate().skip(1) {
258 let index = position as u16;
259 match slot {
260 ConstantSlot::Reserved => {
261 return Err(ConstantPoolError::new(
262 ConstantPoolErrorKind::InvalidLayout,
263 index,
264 None,
265 "reserved slot is only legal at index zero",
266 ));
267 }
268 ConstantSlot::Unusable => {
269 if !matches!(
270 self.slots.get(position - 1),
271 Some(ConstantSlot::Entry(Constant::Long(_) | Constant::Double(_)))
272 ) {
273 return Err(ConstantPoolError::new(
274 ConstantPoolErrorKind::InvalidLayout,
275 index,
276 None,
277 "unusable slot must follow a long or double",
278 ));
279 }
280 }
281 ConstantSlot::Entry(value) => {
282 if matches!(
283 self.slots.get(position.wrapping_add(1)),
284 Some(ConstantSlot::Unusable)
285 ) != matches!(value, Constant::Long(_) | Constant::Double(_))
286 {
287 return Err(ConstantPoolError::new(
288 ConstantPoolErrorKind::InvalidLayout,
289 index,
290 None,
291 "long and double entries must own exactly one following unusable slot",
292 ));
293 }
294 let minimum_major = match value {
295 Constant::MethodHandle { .. }
296 | Constant::MethodType { .. }
297 | Constant::InvokeDynamic { .. } => Some(51),
298 Constant::Module { .. } | Constant::Package { .. } => Some(53),
299 Constant::Dynamic { .. } => Some(55),
300 _ => None,
301 };
302 if let Some(minimum_major) = minimum_major
303 && major_version < minimum_major
304 {
305 return Err(ConstantPoolError::new(
306 ConstantPoolErrorKind::Version,
307 index,
308 None,
309 format!(
310 "constant at index {index} requires classfile major version {minimum_major}"
311 ),
312 ));
313 }
314 validate_constant(self, index, value, major_version)?;
315 }
316 }
317 }
318 Ok(())
319 }
320
321 pub fn encode(
323 &self,
324 writer: &mut ByteWriter,
325 major_version: u16,
326 ) -> Result<(), ConstantPoolError> {
327 self.validate(major_version)?;
328 writer
329 .write_u2(self.slots.len() as u16)
330 .map_err(|e| ConstantPoolError::bytes(0, e))?;
331 for (position, slot) in self.slots.iter().enumerate().skip(1) {
332 if let ConstantSlot::Entry(value) = slot {
333 encode_constant(writer, position as u16, value)?;
334 }
335 }
336 Ok(())
337 }
338}