use super::*; use crate::ParseErr; /// /// An index into the constant pool of the class /// `T` -> What type the target value is supposed to be. Create an enum if multiple values can be there /// /// The value this is pointing at must *always* be a entry of the correct type T /// Type checking is done at parse time, so that the value can be get with minimal overhead #[repr(transparent)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct FromPool { inner: u2, _marker: PhantomData T>, } // could probably be derived if I chose a better marker impl Copy for FromPool {} impl From for FromPool { fn from(n: u2) -> Self { Self { inner: n, _marker: PhantomData, } } } impl FromPool { #[inline] pub const fn inner(&self) -> u2 { self.inner } } impl<'pool, T> FromPool where T: FromCpInfo<'pool>, { #[inline] pub fn get(&self, pool: &'pool [CpInfo]) -> T::Target { T::from_cp_info(&pool[self.inner as usize - 1]) } } impl<'pool, T> FromPool> where T: FromCpInfo<'pool>, { #[inline] pub fn maybe_get(&self, pool: &'pool [CpInfo]) -> Option { if self.inner == 0 { None } else { Some(T::from_cp_info(&pool[self.inner as usize - 1])) } } } pub trait FromCpInfo<'pool> { type Target; fn from_cp_info(info: &'pool CpInfo) -> Self::Target; fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result; } macro_rules! impl_try_from_cp { ($($name:ident),*) => { $( impl<'pool> FromCpInfo<'pool> for $name { type Target = &'pool Self; #[inline] fn from_cp_info(info: &'pool CpInfo) -> Self::Target { match &info.inner { CpInfoInner::$name(class) => class, _kind => unreachable!(), } } #[inline] fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result { if index == 0 { return Err(ParseErr("Index must not be 0".to_string())); } match &info[index as usize - 1].inner { CpInfoInner::$name(class) => Ok(class), kind => Err(ParseErr(format!( concat!("Expected '", stringify!($name), "', found '{:?}'"), kind ))), } } } )* }; } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Class { /// Entry must be `Utf8` pub name_index: FromPool, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Fieldref { /// May be a class or interface type pub class_index: FromPool, /// Entry must be `NameAndType` pub name_and_type_index: FromPool, } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct MethodRef { /// Must be a class type pub class_index: FromPool, /// Entry must be `NameAndType` pub name_and_type_index: FromPool, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct InterfaceMethodref { /// Must be an interface type pub class_index: FromPool, /// Entry must be `NameAndType` pub name_and_type_index: FromPool, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct String { /// Entry must be `Utf8` pub string_index: FromPool, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Integer { // Big endian pub bytes: u4, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Float { /// IEEE 754 floating-point single format, big endian pub bytes: u4, } /// 8 byte constants take up two spaces in the constant pool #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Long { /// Big endian pub high_bytes: u4, /// Big endian pub low_bytes: u4, } /// 8 byte constants take up two spaces in the constant pool #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Double { /// IEEE 754 floating-point double format, big endian pub high_bytes: u4, /// IEEE 754 floating-point double format, big endian pub low_bytes: u4, } /// Any field or method, without the class it belongs to #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct NameAndType { /// Entry must be `Utf8` pub name_index: FromPool, /// Entry must be `Utf8` pub descriptor_index: FromPool, } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Utf8 { /// Contains modified UTF-8 pub bytes: std::string::String, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct MethodHandle { /// The kind of method handle (0-9) /// If the kind is 1-4, the entry must be `FieldRef`. If the kind is 5-8, the entry must be `MethodRef` /// If the kind is 9, the entry must be `InterfaceMethodRef` pub reference_kind: u1, pub reference_index: MethodHandleIndex, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum MethodHandleIndex { Field(FromPool), Method(FromPool), Interface(FromPool), } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct MethodType { /// Entry must be `Utf8` pub descriptor_index: FromPool, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct InvokeDynamic { /// Must be a valid index into the `bootstrap_methods` array of the bootstrap method table of this class field pub bootstrap_method_attr_index: u2, /// Entry must `NameAndType` pub name_and_type_index: FromPool, } // default implementations impl_try_from_cp!( Class, Fieldref, MethodRef, InterfaceMethodref, String, Integer, Float, Long, Double, NameAndType, MethodHandle, MethodType, InvokeDynamic ); // custom implementations impl<'pool> FromCpInfo<'pool> for Utf8 { type Target = &'pool str; #[inline] fn from_cp_info(info: &'pool CpInfo) -> Self::Target { match &info.inner { CpInfoInner::Utf8(class) => &class.bytes, _ => unreachable!(), } } #[inline] fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result { if index == 0 { return Err(ParseErr("Index must not be 0".to_string())); } match &info[index as usize - 1].inner { CpInfoInner::Utf8(class) => Ok(&class.bytes), kind => Err(ParseErr(format!( concat!("Expected '", stringify!($name), "', found '{:?}'"), kind ))), } } }