jbcrs_basic/
constpool.rs

1use std::hash::{Hash, Hasher};
2use std::collections::HashMap;
3use std::cmp::{Eq, PartialEq};
4use std::rc::Rc;
5use std::slice::Iter;
6
7use result::*;
8
9/// A constant pool item
10#[derive(Debug, Clone)]
11pub enum Item {
12    /// An UTF-8 encoded string.
13    /// Inside the class file itself, a modified format is used.
14    UTF8(String),
15    /// An `int`.
16    Integer(i32),
17    /// A `float`.
18    Float(f32),
19    /// A `long`.
20    /// Takes two spots, instead of one.
21    Long(i64),
22    /// A `double`.
23    /// Takes two spots, instead of one.
24    Double(f64),
25    /// An index to the name of the class, or the descriptor of an array type.
26    /// Always refers to an `Item::UTF8(_)`.
27    Class(u16),
28    /// A `java.lang.String` object.
29    String(u16),
30    /// Describes a field reference.
31    FieldRef {
32        /// The index to an `Item::Class(_)`.
33        /// Can be either a Class, or an Interface.
34        class: u16,
35        /// The index to an `Item::NameAndType { .. }`.
36        name_and_type: u16,
37    },
38    /// Describes a method reference.
39    MethodRef {
40        /// The index to an `Item::Class(_)`.
41        /// Must be a Class.
42        class: u16,
43        /// The index to an `Item::NameAndType { .. }`.
44        name_and_type: u16,
45    },
46    /// Describes a method reference, where the class is an interface.
47    InterfaceMethodRef {
48        /// The index to an `Item::Class(_)`.
49        /// Must be an Interface.
50        class: u16,
51        /// The index to an `Item::NameAndType { .. }`.
52        name_and_type: u16,
53    },
54    /// Represents a field or method, without indicating which class or type it belongs to.
55    NameAndType {
56        /// The index to an `Item::UTF8(_)`.
57        /// Must either be a valid unqualfied name or `<init>`.
58        name: u16,
59        /// The index to an `Item::UTF8(_)`.
60        /// Represents a valid field or method descriptor.
61        desc: u16,
62    },
63    /// Represents a method handle
64    MethodHandle {
65        /// Characterizes its bytecode behaviour.
66        kind: ReferenceKind,
67        /// If kind is either GetField, GetStatic, PutField, or PutStatic,
68        /// the entry at that index must be a `Item::FieldRef { .. }`.
69        /// If kind is InvokeVirtual or InvokeSpecial,
70        /// the entry at that index must be `Item::MethodRef { .. }`.
71        /// If kind is InvokeStatic or InvokeSpecial
72        /// and the version of the class is less than 52.0,
73        /// the entry at that index must be `Item::MethodRef { .. }`.
74        /// If it is 52.0 or above,
75        /// it must either be a MethodRef or an `Item::InterfaceMethodRef { .. }`.
76        /// If kind is InvokeInterface,
77        /// the entry at that index must be an `Item::InterfaceMethodRef { .. }`.
78        index: u16,
79    },
80    /// Describes a method type.
81    /// The entry at that index must be an `Item::UTF8(_)`
82    /// representing a method descriptor.
83    MethodType(u16),
84    /// Describes a invoke dynamic instruction,
85    /// and specifies information regarding the bootstrap method.
86    InvokeDynamic {
87        /// The index to an entry of the BootstrapMethods attribute of the class file.
88        bootstrap_method: u16,
89        /// The index to an `Item::NameAndType { .. }`.
90        name_and_type: u16,
91    },
92    /// Represents a module.
93    /// The entry at that index must be a `Item::UTF8(_)` with a valid name.
94    /// The class must have the MODULE flag set.
95    Module(u16),
96    /// Represents a package exported or opened by a module.
97    /// The entry at that index must be an `Item::UTF8(_)`
98    /// with a valid package name encoded in internal form.
99    /// The class must have the MODULE flag set.
100    Package(u16),
101}
102
103impl Item {
104    /// Returns true if this item takes up two spaces, false otherwise.
105    fn is_double(&self) -> bool {
106        match *self {
107            Item::Long(_) | Item::Double(_) => true,
108            _ => false,
109        }
110    }
111}
112
113// Implementing `Hash` and `Eq` manually (sorry for this awful mess of code),
114// since `Item` contains f32 and f64, which by default can't be hashed.
115// This is good normally, but here we are okay
116// to have multiple f32 or f64,
117// which are not equal bitwise but contextwise.
118
119impl Hash for Item {
120    fn hash<H: Hasher>(&self, state: &mut H) {
121        match *self {
122            Item::UTF8(ref s) => {
123                state.write_u8(1);
124                s.hash(state);
125            }
126            Item::Integer(i) => {
127                state.write_u8(3);
128                i.hash(state);
129            }
130            Item::Float(f) => {
131                state.write_u8(4);
132                f.to_bits().hash(state);
133            }
134            Item::Long(i) => {
135                state.write_u8(5);
136                i.hash(state);
137            }
138            Item::Double(f) => {
139                state.write_u8(6);
140                f.to_bits().hash(state);
141            }
142            Item::Class(ptr) => {
143                state.write_u8(7);
144                ptr.hash(state);
145            }
146            Item::String(ptr) => {
147                state.write_u8(8);
148                ptr.hash(state);
149            }
150            Item::FieldRef {
151                class,
152                name_and_type,
153            } => {
154                state.write_u8(9);
155                class.hash(state);
156                name_and_type.hash(state);
157            }
158            Item::MethodRef {
159                class,
160                name_and_type,
161            } => {
162                state.write_u8(10);
163                class.hash(state);
164                name_and_type.hash(state);
165            }
166            Item::InterfaceMethodRef {
167                class,
168                name_and_type,
169            } => {
170                state.write_u8(11);
171                class.hash(state);
172                name_and_type.hash(state);
173            }
174            Item::NameAndType { name, desc } => {
175                state.write_u8(12);
176                name.hash(state);
177                desc.hash(state);
178            }
179            Item::MethodHandle { ref kind, index } => {
180                state.write_u8(15);
181                kind.hash(state);
182                index.hash(state);
183            }
184            Item::MethodType(ptr) => {
185                state.write_u8(16);
186                ptr.hash(state);
187            }
188            Item::InvokeDynamic {
189                bootstrap_method,
190                name_and_type,
191            } => {
192                state.write_u8(18);
193                bootstrap_method.hash(state);
194                name_and_type.hash(state);
195            }
196            Item::Module(ptr) => {
197                state.write_u8(19);
198                ptr.hash(state);
199            }
200            Item::Package(ptr) => {
201                state.write_u8(20);
202                ptr.hash(state);
203            }
204        }
205    }
206}
207
208impl PartialEq for Item {
209    fn eq(&self, other: &Item) -> bool {
210        match (self, other) {
211            (&Item::UTF8(ref str1), &Item::UTF8(ref str2)) => *str1 == *str2,
212            (&Item::Integer(i1), &Item::Integer(i2)) => i1 == i2,
213            (&Item::Float(f1), &Item::Float(f2)) => f1.to_bits() == f2.to_bits(),
214            (&Item::Long(i1), &Item::Long(i2)) => i1 == i2,
215            (&Item::Double(f1), &Item::Double(f2)) => f1.to_bits() == f2.to_bits(),
216            (&Item::Class(i1), &Item::Class(i2)) | (&Item::String(i1), &Item::String(i2)) => {
217                i1 == i2
218            }
219            (
220                &Item::FieldRef {
221                    class: class1,
222                    name_and_type: nat1,
223                },
224                &Item::FieldRef {
225                    class: class2,
226                    name_and_type: nat2,
227                },
228            )
229            | (
230                &Item::MethodRef {
231                    class: class1,
232                    name_and_type: nat1,
233                },
234                &Item::MethodRef {
235                    class: class2,
236                    name_and_type: nat2,
237                },
238            )
239            | (
240                &Item::InterfaceMethodRef {
241                    class: class1,
242                    name_and_type: nat1,
243                },
244                &Item::InterfaceMethodRef {
245                    class: class2,
246                    name_and_type: nat2,
247                },
248            ) => class1 == class2 && nat1 == nat2,
249            (
250                &Item::NameAndType {
251                    name: name1,
252                    desc: desc1,
253                },
254                &Item::NameAndType {
255                    name: name2,
256                    desc: desc2,
257                },
258            ) => name1 == name2 && desc1 == desc2,
259            (
260                &Item::MethodHandle {
261                    kind: ref kind1,
262                    index: index1,
263                },
264                &Item::MethodHandle {
265                    kind: ref kind2,
266                    index: index2,
267                },
268            ) => kind1 == kind2 && index1 == index2,
269            (
270                &Item::InvokeDynamic {
271                    bootstrap_method: bma1,
272                    name_and_type: nat1,
273                },
274                &Item::InvokeDynamic {
275                    bootstrap_method: bma2,
276                    name_and_type: nat2,
277                },
278            ) => bma1 == bma2 && nat1 == nat2,
279            (&Item::Package(index1), &Item::Package(index2))
280            | (&Item::Module(index1), &Item::Module(index2))
281            | (&Item::MethodType(index1), &Item::MethodType(index2)) => index1 == index2,
282
283            _ => false,
284        }
285    }
286}
287
288impl Eq for Item {}
289
290#[derive(Eq, PartialEq, Hash, Debug, Clone)]
291pub enum ReferenceKind {
292    GetField,
293    GetStatic,
294    PutField,
295    PutStatic,
296    InvokeVirtual,
297    InvokeStatic,
298    InvokeSpecial,
299    NewInvokeSpecial,
300    InvokeInterface,
301}
302
303/// The constant pool found in every java class file.
304/// It is used to have fast lookup for entries and small files.
305/// Removing or modifying items is not allowed
306/// to respect already 'used' indices
307/// or to prevent rehashing of the underlying `HashMap`.
308///
309/// A `Vec` and a `HashMap` is used internally to have fast lookups by index and value.
310/// To connect both, `Rc`s are used.
311/// As a result, we will have a little overhead, but this should be negligible.
312#[derive(Default)]
313pub struct Pool {
314    length: u16,
315    by_index: Vec<Option<Rc<Item>>>,
316    by_entry: HashMap<Rc<Item>, u16>,
317}
318
319impl Pool {
320    pub fn new() -> Self {
321        Pool {
322            length: 1,
323            by_index: Vec::new(),
324            by_entry: HashMap::new(),
325        }
326    }
327
328    pub fn with_capacity(cap: u16) -> Self {
329        Pool {
330            length: 1,
331            by_index: Vec::with_capacity(cap as usize),
332            by_entry: HashMap::with_capacity(cap as usize),
333        }
334    }
335
336    /// Returns the *encoded* length of the pool.
337    #[inline]
338    pub fn len(&self) -> u16 {
339        self.length
340    }
341
342    /// Returns true if the pool is empty, false otherwise.
343    #[inline]
344    pub fn is_empty(&self) -> bool {
345        self.len() == 1
346    }
347
348    /// Returns the item at a specified index.
349    /// If the index is 0 or greater than the size of the pool, an error is returned.
350    pub fn get(&self, index: u16) -> Result<&Item> {
351        // bounds checking
352        if index != 0 && index <= self.len() {
353            if let Some(ref item) = self.by_index[index as usize - 1] {
354                return Ok(item);
355            }
356        }
357
358        Err(Error::InvalidCPItem(index))
359    }
360
361    /// Returns a cloned String at a specified index.
362    pub fn get_utf8(&self, index: u16) -> Result<String> {
363        if let Item::UTF8(ref s) = *self.get(index)? {
364            Ok(s.clone())
365        } else {
366            Err(Error::InvalidCPItem(index))
367        }
368    }
369
370    /// Returns a class name at a specified index,
371    /// but if the utf index is 0, None is returned.
372    pub fn get_class_name_opt(&self, index: u16) -> Result<Option<String>> {
373        if let Item::Class(utf_index) = *self.get(index)? {
374            if utf_index == 0 {
375                Ok(None)
376            } else {
377                Ok(Some(self.get_utf8(utf_index)?))
378            }
379        } else {
380            Err(Error::InvalidCPItem(index))
381        }
382    }
383
384    /// Returns a class name at a specified index.
385    pub fn get_class_name(&self, index: u16) -> Result<String> {
386        if let Item::Class(utf_index) = *self.get(index)? {
387            self.get_utf8(utf_index)
388        } else {
389            Err(Error::InvalidCPItem(index))
390        }
391    }
392
393    /// Pushes an item on the pool.
394    pub fn push(&mut self, item: Item) -> Result<u16> {
395        if self.len() == u16::max_value() {
396            return Err(Error::CPTooLarge);
397        }
398
399        let double = item.is_double();
400        let length = &mut self.length;
401
402        let rc_item = Rc::new(item);
403        let rc_item1 = Rc::clone(&rc_item);
404
405        let by_index = &mut self.by_index;
406
407        // check if in pool, if not insert it
408        Ok(*self.by_entry.entry(rc_item).or_insert_with(move || {
409            by_index.push(Some(Rc::clone(&rc_item1)));
410
411            let prev_length = *length;
412            if double {
413                // long and double take an additional space
414                by_index.push(None);
415                *length += 2;
416            } else {
417                *length += 1;
418            }
419            prev_length
420        }))
421    }
422
423    /// Pushes an item, which might be a duplicate.
424    /// This removes the possibility of reading a class,
425    /// which has multiple constant pool entries, which are the same
426    /// and then accessing the wrong entry.
427    pub fn push_duplicate(&mut self, item: Item) -> Result<u16> {
428        if self.len() == u16::max_value() {
429            return Err(Error::CPTooLarge);
430        }
431
432        let double = item.is_double();
433        let length = self.length;
434        let rc_item = Rc::new(item);
435
436        self.by_index.push(Some(Rc::clone(&rc_item)));
437        if double {
438            self.by_index.push(None);
439            self.length += 2;
440        } else {
441            self.length += 1;
442        }
443
444        self.by_entry.insert(rc_item, length);
445        Ok(length)
446    }
447
448    /// Pushes a new UTF-8 item on the pool and returns an index to it.
449    pub fn push_utf8(&mut self, content: String) -> Result<u16> {
450        self.push(Item::UTF8(content))
451    }
452
453    /// Pushes a new class item on the pool and returns an index to it.
454    pub fn push_class(&mut self, name: String) -> Result<u16> {
455        let name_index = self.push_utf8(name)?;
456        self.push(Item::Class(name_index))
457    }
458
459    pub fn iter(&self) -> PoolIter {
460        PoolIter {
461            iter: self.by_index.iter(),
462            index: 0,
463        }
464    }
465}
466
467// implement later again (maybe)
468//impl Clone for Pool {
469//    fn clone(&self) -> Pool {
470//        Pool {
471//            content: self.content.clone(),
472//        }
473//    }
474//}
475
476/// Iterates over all the elements in the constant pool
477/// It basically is a filter with a different name
478pub struct PoolIter<'a> {
479    iter: Iter<'a, Option<Rc<Item>>>,
480    index: u16,
481}
482
483impl<'a> Iterator for PoolIter<'a> {
484    type Item = (u16, &'a Item);
485
486    fn next(&mut self) -> Option<Self::Item> {
487        self.index += 1;
488        if let Some(rc_item) = self.iter.next() {
489            if let Some(ref item) = *rc_item {
490                Some((self.index, item))
491            } else {
492                self.next()
493            }
494        } else {
495            None
496        }
497    }
498}
499
500#[cfg(test)]
501mod tests {
502    use super::*;
503
504    #[test]
505    fn push_and_get() {
506        let mut pool = Pool::new();
507        assert_eq!(pool.push(Item::Integer(123)).unwrap(), 1);
508        assert_eq!(pool.push(Item::Long(32767)).unwrap(), 2);
509        assert_eq!(pool.push(Item::Long(65535)).unwrap(), 4);
510        assert_eq!(pool.push(Item::Float(3.8)).unwrap(), 6);
511        assert_eq!(pool.len(), 7);
512        assert_eq!(pool.push(Item::Integer(123)).unwrap(), 1);
513        assert_eq!(pool.len(), 7);
514
515        assert_eq!(pool.get(1).unwrap(), &Item::Integer(123));
516        assert_eq!(pool.get(2).unwrap(), &Item::Long(32767));
517        assert_eq!(pool.get(4).unwrap(), &Item::Long(65535));
518        assert_eq!(pool.get(6).unwrap(), &Item::Float(3.8));
519
520        let mut iter = pool.iter();
521        assert_eq!(iter.next(), Some((1, &Item::Integer(123))));
522        assert_eq!(iter.next(), Some((2, &Item::Long(32767))));
523        assert_eq!(iter.next(), Some((4, &Item::Long(65535))));
524        assert_eq!(iter.next(), Some((6, &Item::Float(3.8))));
525        assert_eq!(iter.next(), None);
526    }
527
528    #[test]
529    fn push_duplicate() {
530        let mut pool = Pool::new();
531        assert_eq!(pool.push_duplicate(Item::Integer(123)).unwrap(), 1);
532        assert_eq!(pool.push_duplicate(Item::Long(32767)).unwrap(), 2);
533        assert_eq!(pool.push_duplicate(Item::Long(65535)).unwrap(), 4);
534        assert_eq!(pool.push_duplicate(Item::Float(3.8)).unwrap(), 6);
535        assert_eq!(pool.len(), 7);
536        assert_eq!(pool.push_duplicate(Item::Integer(123)).unwrap(), 7);
537        assert_eq!(pool.len(), 8);
538
539        assert_eq!(pool.get(1).unwrap(), &Item::Integer(123));
540        assert_eq!(pool.get(2).unwrap(), &Item::Long(32767));
541        assert_eq!(pool.get(4).unwrap(), &Item::Long(65535));
542        assert_eq!(pool.get(6).unwrap(), &Item::Float(3.8));
543        assert_eq!(pool.get(7).unwrap(), &Item::Integer(123));
544    }
545}