coldsquare/crates/file-parser/src/model/cp_info.rs
Nilstrieb 8376d22aa6 moving to Java SE16
everything should be done except the attributes
2021-08-28 23:51:43 +02:00

326 lines
8.8 KiB
Rust

use crate::{u1, u2, u4, CpInfo, CpInfoInner, ParseErr};
use std::marker::PhantomData;
///
/// 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<T> {
inner: u2,
_marker: PhantomData<fn() -> T>,
}
// could probably be derived if I chose a better marker
impl<T: Clone> Copy for FromPool<T> {}
impl<T> From<u2> for FromPool<T> {
#[inline]
fn from(n: u2) -> Self {
Self {
inner: n,
_marker: PhantomData,
}
}
}
impl<T> FromPool<T> {
#[inline]
pub const fn inner(&self) -> u2 {
self.inner
}
}
impl<'pool, T> FromPool<T>
where
T: FromCpInfo<'pool>,
{
#[inline]
pub fn get(&self, pool: &'pool [CpInfo]) -> T::Target {
T::from_cp_info_with_index(pool, self.inner)
}
}
impl<'pool, T> FromPool<Option<T>>
where
T: FromCpInfo<'pool>,
{
#[inline]
pub fn maybe_get(&self, pool: &'pool [CpInfo]) -> Option<T::Target> {
if self.inner == 0 {
None
} else {
Some(T::from_cp_info_with_index(pool, self.inner))
}
}
}
pub trait ValidateCpInfo {
/// check that the constant pool entry has the correct type
/// `index` is the original, non-null index (it can be 0 optional constants)
fn validate_cp_info(info: &[CpInfo], index: u2) -> Result<(), ParseErr>;
}
pub trait FromCpInfo<'pool>: ValidateCpInfo {
type Target;
fn from_cp_info(info: &'pool CpInfo) -> Self::Target;
fn from_cp_info_with_index(info: &'pool [CpInfo], index: u2) -> Self::Target {
Self::from_cp_info(&info[index as usize - 1])
}
}
impl<'pool, T> FromCpInfo<'pool> for Option<T>
where
T: FromCpInfo<'pool>,
{
type Target = Option<T::Target>;
#[inline]
fn from_cp_info(_info: &'pool CpInfo) -> Self::Target {
unreachable!("FromPool<Option<T>> should always be get through `from_cp_info_with_index`")
}
fn from_cp_info_with_index(info: &'pool [CpInfo], index: u2) -> Self::Target {
if index == 0 {
None
} else {
Some(T::from_cp_info_with_index(info, index))
}
}
}
impl<T> ValidateCpInfo for Option<T>
where
T: ValidateCpInfo,
{
fn validate_cp_info(info: &[CpInfo], index: u2) -> Result<(), ParseErr> {
if index == 0 {
Ok(())
} else {
T::validate_cp_info(info, index)
}
}
}
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!(),
}
}
}
impl ValidateCpInfo for $name {
fn validate_cp_info(info: &[CpInfo], index: u2) -> Result<(), ParseErr> {
if index == 0 {
return Err(ParseErr("Index must not be 0".to_string()));
}
// todo this here might actually be an empty constant pool depending on whether is is still parsing the constant pool
// it needs to be checked after testing
// not now
// pls
// i hate this
match &info[index as usize - 1].inner {
CpInfoInner::$name(_) => Ok(()),
kind => Err(ParseErr(format!(
concat!("Expected '", stringify!($name), "', found '{:?}'"),
kind
))),
}
}
}
)*
};
}
impl<'pool> FromCpInfo<'pool> for CpInfoInner {
type Target = &'pool Self;
fn from_cp_info(info: &'pool CpInfo) -> Self::Target {
&info.inner
}
}
impl ValidateCpInfo for CpInfoInner {
fn validate_cp_info(_info: &[CpInfo], _index: u2) -> Result<(), ParseErr> {
Ok(())
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Class {
pub name_index: FromPool<Utf8>,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Fieldref {
/// May be a class or interface type
pub class_index: FromPool<Class>,
pub name_and_type_index: FromPool<NameAndType>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct MethodRef {
/// Must be a class type
pub class_index: FromPool<Class>,
pub name_and_type_index: FromPool<NameAndType>,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct InterfaceMethodref {
/// Must be an interface type
pub class_index: FromPool<Class>,
pub name_and_type_index: FromPool<NameAndType>,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct String {
pub string_index: FromPool<Utf8>,
}
#[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 {
pub name_index: FromPool<Utf8>,
pub descriptor_index: FromPool<Utf8>,
}
#[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<Fieldref>),
Method(FromPool<MethodRef>),
Interface(FromPool<InterfaceMethodref>),
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct MethodType {
pub descriptor_index: FromPool<Utf8>,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Dynamic {
/// 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,
pub name_and_type_index: FromPool<NameAndType>,
}
#[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,
pub name_and_type_index: FromPool<NameAndType>,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Module {
pub name_index: FromPool<Utf8>,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Package {
pub name_index: FromPool<Utf8>,
}
// default implementations
impl_try_from_cp!(
Class,
Fieldref,
MethodRef,
InterfaceMethodref,
String,
Integer,
Float,
Long,
Double,
NameAndType,
MethodHandle,
MethodType,
Dynamic,
InvokeDynamic,
Module,
Package
);
impl ValidateCpInfo for Utf8 {
fn validate_cp_info(info: &[CpInfo], index: u2) -> Result<(), ParseErr> {
if index == 0 {
return Err(ParseErr("Index must not be 0".to_string()));
}
match &info[index as usize - 1].inner {
CpInfoInner::Utf8(_) => Ok(()),
kind => Err(ParseErr(format!(
concat!("Expected '", stringify!($name), "', found '{:?}'"),
kind
))),
}
}
}
// 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!(),
}
}
}