Skip to main content

java_class_rs/
constant_pool.rs

1use nom::{
2    bytes::complete::take,
3    combinator::map,
4    number::complete::{be_f32, be_f64, be_u16, be_u32, be_u64, be_u8},
5    IResult,
6};
7
8use crate::types::*;
9
10pub fn parse_constant_pool(input: &[u8], count: u16) -> IResult<&[u8], Vec<ConstantPoolEntry>> {
11    let mut entries = Vec::with_capacity(count as usize);
12    // Index 0 is unused, but we skip it; entries are 1-indexed.
13    // We parse count-1 entries (index 1 to count-1).
14    let mut remaining = input;
15    let mut i = 1u16;
16    while i < count {
17        let (rem, entry) = parse_constant_pool_entry(remaining)?;
18        let takes_two = entry.takes_two_slots();
19        entries.push(entry);
20        i += 1;
21        if takes_two {
22            // Long and Double occupy two slots; the second slot is unusable.
23            i += 1;
24        }
25        remaining = rem;
26    }
27    Ok((remaining, entries))
28}
29
30fn parse_constant_pool_entry(input: &[u8]) -> IResult<&[u8], ConstantPoolEntry> {
31    let (input, tag) = be_u8(input)?;
32    match tag {
33        cp_tag::UTF8 => map(parse_utf8_info, ConstantPoolEntry::Utf8)(input),
34        cp_tag::INTEGER => map(parse_integer_info, ConstantPoolEntry::Integer)(input),
35        cp_tag::FLOAT => map(parse_float_info, ConstantPoolEntry::Float)(input),
36        cp_tag::LONG => map(parse_long_info, ConstantPoolEntry::Long)(input),
37        cp_tag::DOUBLE => map(parse_double_info, ConstantPoolEntry::Double)(input),
38        cp_tag::CLASS => map(parse_class_info, ConstantPoolEntry::Class)(input),
39        cp_tag::STRING => map(parse_string_info, ConstantPoolEntry::String)(input),
40        cp_tag::FIELDREF => map(parse_fieldref_info, ConstantPoolEntry::Fieldref)(input),
41        cp_tag::METHODREF => map(parse_methodref_info, ConstantPoolEntry::Methodref)(input),
42        cp_tag::INTERFACE_METHODREF => map(
43            parse_interface_methodref_info,
44            ConstantPoolEntry::InterfaceMethodref,
45        )(input),
46        cp_tag::NAME_AND_TYPE => {
47            map(parse_name_and_type_info, ConstantPoolEntry::NameAndType)(input)
48        }
49        cp_tag::METHOD_HANDLE => {
50            map(parse_method_handle_info, ConstantPoolEntry::MethodHandle)(input)
51        }
52        cp_tag::METHOD_TYPE => map(parse_method_type_info, ConstantPoolEntry::MethodType)(input),
53        cp_tag::DYNAMIC => map(parse_dynamic_info, ConstantPoolEntry::Dynamic)(input),
54        cp_tag::INVOKE_DYNAMIC => {
55            map(parse_invoke_dynamic_info, ConstantPoolEntry::InvokeDynamic)(input)
56        }
57        cp_tag::MODULE => map(parse_module_info, ConstantPoolEntry::Module)(input),
58        cp_tag::PACKAGE => map(parse_package_info, ConstantPoolEntry::Package)(input),
59        _ => Err(nom::Err::Error(nom::error::Error::new(
60            input,
61            nom::error::ErrorKind::Tag,
62        ))),
63    }
64}
65
66fn parse_utf8_info(input: &[u8]) -> IResult<&[u8], Utf8Info> {
67    let (input, length) = be_u16(input)?;
68    let (input, bytes) = take(length as usize)(input)?;
69    // Modified UTF-8 decoding (CESU-8)
70    let value = cesu8::from_java_cesu8(bytes)
71        .map(|s| s.into_owned())
72        .unwrap_or_else(|_| String::from_utf8_lossy(bytes).into_owned());
73    Ok((input, Utf8Info { value }))
74}
75
76fn parse_integer_info(input: &[u8]) -> IResult<&[u8], IntegerInfo> {
77    let (input, value) = be_u32(input)?;
78    Ok((
79        input,
80        IntegerInfo {
81            value: value as i32,
82        },
83    ))
84}
85
86fn parse_float_info(input: &[u8]) -> IResult<&[u8], FloatInfo> {
87    let (input, value) = be_f32(input)?;
88    Ok((input, FloatInfo { value }))
89}
90
91fn parse_long_info(input: &[u8]) -> IResult<&[u8], LongInfo> {
92    let (input, value) = be_u64(input)?;
93    Ok((
94        input,
95        LongInfo {
96            value: value as i64,
97        },
98    ))
99}
100
101fn parse_double_info(input: &[u8]) -> IResult<&[u8], DoubleInfo> {
102    let (input, value) = be_f64(input)?;
103    Ok((input, DoubleInfo { value }))
104}
105
106fn parse_class_info(input: &[u8]) -> IResult<&[u8], ClassInfo> {
107    let (input, name_index) = be_u16(input)?;
108    Ok((input, ClassInfo { name_index }))
109}
110
111fn parse_string_info(input: &[u8]) -> IResult<&[u8], StringInfo> {
112    let (input, string_index) = be_u16(input)?;
113    Ok((input, StringInfo { string_index }))
114}
115
116fn parse_fieldref_info(input: &[u8]) -> IResult<&[u8], FieldrefInfo> {
117    let (input, class_index) = be_u16(input)?;
118    let (input, name_and_type_index) = be_u16(input)?;
119    Ok((
120        input,
121        FieldrefInfo {
122            class_index,
123            name_and_type_index,
124        },
125    ))
126}
127
128fn parse_methodref_info(input: &[u8]) -> IResult<&[u8], MethodrefInfo> {
129    let (input, class_index) = be_u16(input)?;
130    let (input, name_and_type_index) = be_u16(input)?;
131    Ok((
132        input,
133        MethodrefInfo {
134            class_index,
135            name_and_type_index,
136        },
137    ))
138}
139
140fn parse_interface_methodref_info(input: &[u8]) -> IResult<&[u8], InterfaceMethodrefInfo> {
141    let (input, class_index) = be_u16(input)?;
142    let (input, name_and_type_index) = be_u16(input)?;
143    Ok((
144        input,
145        InterfaceMethodrefInfo {
146            class_index,
147            name_and_type_index,
148        },
149    ))
150}
151
152fn parse_name_and_type_info(input: &[u8]) -> IResult<&[u8], NameAndTypeInfo> {
153    let (input, name_index) = be_u16(input)?;
154    let (input, descriptor_index) = be_u16(input)?;
155    Ok((
156        input,
157        NameAndTypeInfo {
158            name_index,
159            descriptor_index,
160        },
161    ))
162}
163
164fn parse_method_handle_info(input: &[u8]) -> IResult<&[u8], MethodHandleInfo> {
165    let (input, reference_kind) = be_u8(input)?;
166    let (input, reference_index) = be_u16(input)?;
167    Ok((
168        input,
169        MethodHandleInfo {
170            reference_kind,
171            reference_index,
172        },
173    ))
174}
175
176fn parse_method_type_info(input: &[u8]) -> IResult<&[u8], MethodTypeInfo> {
177    let (input, descriptor_index) = be_u16(input)?;
178    Ok((input, MethodTypeInfo { descriptor_index }))
179}
180
181fn parse_dynamic_info(input: &[u8]) -> IResult<&[u8], DynamicInfo> {
182    let (input, bootstrap_method_attr_index) = be_u16(input)?;
183    let (input, name_and_type_index) = be_u16(input)?;
184    Ok((
185        input,
186        DynamicInfo {
187            bootstrap_method_attr_index,
188            name_and_type_index,
189        },
190    ))
191}
192
193fn parse_invoke_dynamic_info(input: &[u8]) -> IResult<&[u8], InvokeDynamicInfo> {
194    let (input, bootstrap_method_attr_index) = be_u16(input)?;
195    let (input, name_and_type_index) = be_u16(input)?;
196    Ok((
197        input,
198        InvokeDynamicInfo {
199            bootstrap_method_attr_index,
200            name_and_type_index,
201        },
202    ))
203}
204
205fn parse_module_info(input: &[u8]) -> IResult<&[u8], ModuleInfo> {
206    let (input, name_index) = be_u16(input)?;
207    Ok((input, ModuleInfo { name_index }))
208}
209
210fn parse_package_info(input: &[u8]) -> IResult<&[u8], PackageInfo> {
211    let (input, name_index) = be_u16(input)?;
212    Ok((input, PackageInfo { name_index }))
213}
214
215/// Helper to resolve a UTF8 string from the constant pool by index (1-based).
216/// Returns None if the index is out of bounds or not a UTF8 entry.
217pub fn get_utf8(pool: &[ConstantPoolEntry], index: u16) -> Option<&str> {
218    if index == 0 {
219        return None;
220    }
221    // Entries are stored sequentially; we need to account for Long/Double taking 2 slots.
222    let mut slot = 1u16;
223    for entry in pool {
224        if slot == index {
225            if let ConstantPoolEntry::Utf8(utf8) = entry {
226                return Some(&utf8.value);
227            }
228            return None;
229        }
230        slot += 1;
231        if entry.takes_two_slots() {
232            slot += 1;
233        }
234    }
235    None
236}
237
238/// Helper to get a constant pool entry by its logical index (1-based, accounting for Long/Double double-slot).
239pub fn get_entry(pool: &[ConstantPoolEntry], index: u16) -> Option<&ConstantPoolEntry> {
240    if index == 0 {
241        return None;
242    }
243    let mut slot = 1u16;
244    for entry in pool {
245        if slot == index {
246            return Some(entry);
247        }
248        slot += 1;
249        if entry.takes_two_slots() {
250            slot += 1;
251        }
252    }
253    None
254}