Partially fixed constant pool lookups and validation

The code compiles, but the tests still don't work
The problem is that it tries to validate the constant pool while parsing the constant pool. This validation needs to be delayed.
This commit is contained in:
nora 2021-08-26 16:18:06 +02:00
parent a1620476d8
commit 125b880f7d
4 changed files with 128 additions and 73 deletions

View file

@ -1,8 +1,8 @@
use file_parser::{ClassFile, ParseErr}; use file_parser::ClassFile;
use std::error::Error; use std::io;
use std::io::Write; use std::io::Write;
pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dyn Error>> { pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), io::Error> {
let cp = &class.constant_pool; let cp = &class.constant_pool;
writeln!( writeln!(
@ -17,24 +17,21 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
w, w,
"class {} extends {}{} {{", "class {} extends {}{} {{",
&class.this_class.get(cp).name_index.get(cp), &class.this_class.get(cp).name_index.get(cp),
match class.super_class.get(cp) { match class.super_class.maybe_get(cp) {
None => "<none>", None => "<none>",
Some(class) => &class.name_index.get(cp), Some(class) => class.name_index.get(cp),
} },
if class.interfaces.is_empty() { if class.interfaces.is_empty() {
"".to_string() "".to_string()
} else { } else {
format!( format!(
" implements {}", " implements {}",
// this is absolutely terrible but it works i guess
class class
.interfaces .interfaces
.iter() .iter()
.map(|i| i.get(cp)) .map(|i| i.get(cp))
.collect::<Result<Vec<_>, ParseErr>>()?
.iter()
.map(|i| i.name_index.get(cp)) .map(|i| i.name_index.get(cp))
.collect::<Result<Vec<_>, ParseErr>>()? .collect::<Vec<_>>()
.join(",") .join(",")
) )
}, },
@ -42,7 +39,7 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
writeln!(w, " Attributes:")?; writeln!(w, " Attributes:")?;
for attr in &class.attributes { for attr in &class.attributes {
writeln!(w, " {}", &attr.attribute_name_index.get(cp)?)?; writeln!(w, " {}", &attr.attribute_name_index.get(cp))?;
} }
writeln!(w)?; writeln!(w)?;
@ -51,8 +48,8 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
writeln!( writeln!(
w, w,
" {} {}", " {} {}",
&field.descriptor_index.get(cp)?, &field.descriptor_index.get(cp),
&field.name_index.get(cp)? &field.name_index.get(cp)
)?; )?;
} }
writeln!(w)?; writeln!(w)?;
@ -62,8 +59,8 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
writeln!( writeln!(
w, w,
" {} {}", " {} {}",
&method.descriptor_index.get(cp)?, &method.descriptor_index.get(cp),
&method.name_index.get(cp)?, &method.name_index.get(cp),
)?; )?;
} }

View file

@ -2,7 +2,7 @@ mod model;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
use crate::cp_info::FromCpInfo; use crate::cp_info::ValidateCpInfo;
pub use model::*; pub use model::*;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -46,9 +46,9 @@ impl<'a> Data<'a> {
} }
/// Parses a u2 and validates it in the constant pool /// Parses a u2 and validates it in the constant pool
fn cp<'pool, T: FromCpInfo<'pool>>(&mut self, pool: &'pool [CpInfo]) -> Result<FromPool<T>> { fn cp<T: ValidateCpInfo>(&mut self, pool: &[CpInfo]) -> Result<FromPool<T>> {
let index = self.u2()?; let index = self.u2()?;
let _ = T::try_from_cp_info(pool, index)?; T::validate_cp_info(pool, index)?;
Ok(index.into()) Ok(index.into())
} }
@ -117,9 +117,12 @@ macro_rules! parse_primitive {
parse_primitive!(u1, u2, u4); parse_primitive!(u1, u2, u4);
impl<T> Parse for FromPool<T> { impl<T> Parse for FromPool<T>
where
T: ValidateCpInfo,
{
fn parse(data: &mut Data, cp: &[CpInfo]) -> Result<Self> { fn parse(data: &mut Data, cp: &[CpInfo]) -> Result<Self> {
Ok(data.u2()?.into()) data.cp(cp)
} }
} }
@ -129,7 +132,9 @@ impl Parse for ClassFile {
assert_eq!(magic, 0xCAFEBABE); assert_eq!(magic, 0xCAFEBABE);
let minor_version = data.u2()?; let minor_version = data.u2()?;
let major_version = data.u2()?; let major_version = data.u2()?;
dbg!("reached constant pool");
let constant_pool = parse_vec(data.u2()? - 1, data, cp)?; // the minus one is important let constant_pool = parse_vec(data.u2()? - 1, data, cp)?; // the minus one is important
dbg!("after constant pool");
let cp = &constant_pool; let cp = &constant_pool;
let access_flags = data.u2()?; let access_flags = data.u2()?;
let this_class = data.cp(cp)?; let this_class = data.cp(cp)?;
@ -160,6 +165,7 @@ impl Parse for ClassFile {
impl Parse for CpInfo { impl Parse for CpInfo {
fn parse(data: &mut Data, cp: &[CpInfo]) -> Result<Self> { fn parse(data: &mut Data, cp: &[CpInfo]) -> Result<Self> {
let tag = data.u1()?; let tag = data.u1()?;
dbg!(tag);
Ok(match tag { Ok(match tag {
7 => Self { 7 => Self {
@ -302,7 +308,7 @@ impl Parse for AttributeInfo {
} }
impl Parse for AttributeCodeException { impl Parse for AttributeCodeException {
fn parse(data: &mut Data, cp: &[CpInfo]) -> Result<Self> { fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result<Self> {
Ok(Self { Ok(Self {
start_pc: data.last_u2()?, start_pc: data.last_u2()?,
end_pc: data.last_u2()?, end_pc: data.last_u2()?,
@ -357,7 +363,7 @@ impl Parse for StackMapFrame {
} }
impl Parse for VerificationTypeInfo { impl Parse for VerificationTypeInfo {
fn parse(data: &mut Data, cp: &[CpInfo]) -> Result<Self> { fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result<Self> {
let tag = data.u1()?; let tag = data.u1()?;
Ok(match tag { Ok(match tag {
0 => Self::Top { tag }, 0 => Self::Top { tag },
@ -397,7 +403,7 @@ impl Parse for AttributeInnerClass {
} }
impl Parse for AttributeLineNumber { impl Parse for AttributeLineNumber {
fn parse(data: &mut Data, cp: &[CpInfo]) -> Result<Self> { fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result<Self> {
Ok(Self { Ok(Self {
start_pc: data.u2()?, start_pc: data.u2()?,
line_number: data.u2()?, line_number: data.u2()?,

View file

@ -1,5 +1,5 @@
use super::*; use crate::{u1, u2, u4, CpInfo, CpInfoInner, ParseErr};
use crate::ParseErr; use std::marker::PhantomData;
/// ///
/// An index into the constant pool of the class /// An index into the constant pool of the class
@ -18,6 +18,7 @@ pub struct FromPool<T> {
impl<T: Clone> Copy for FromPool<T> {} impl<T: Clone> Copy for FromPool<T> {}
impl<T> From<u2> for FromPool<T> { impl<T> From<u2> for FromPool<T> {
#[inline]
fn from(n: u2) -> Self { fn from(n: u2) -> Self {
Self { Self {
inner: n, inner: n,
@ -39,7 +40,7 @@ where
{ {
#[inline] #[inline]
pub fn get(&self, pool: &'pool [CpInfo]) -> T::Target { pub fn get(&self, pool: &'pool [CpInfo]) -> T::Target {
T::from_cp_info(&pool[self.inner as usize - 1]) T::from_cp_info_with_index(pool, self.inner)
} }
} }
@ -52,15 +53,56 @@ where
if self.inner == 0 { if self.inner == 0 {
None None
} else { } else {
Some(T::from_cp_info(&pool[self.inner as usize - 1])) Some(T::from_cp_info_with_index(pool, self.inner))
} }
} }
} }
pub trait FromCpInfo<'pool> { 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; type Target;
fn from_cp_info(info: &'pool CpInfo) -> Self::Target; fn from_cp_info(info: &'pool CpInfo) -> Self::Target;
fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result<Self::Target, ParseErr>; 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 { macro_rules! impl_try_from_cp {
@ -76,14 +118,20 @@ macro_rules! impl_try_from_cp {
_kind => unreachable!(), _kind => unreachable!(),
} }
} }
}
#[inline] impl ValidateCpInfo for $name {
fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result<Self::Target, ParseErr> { fn validate_cp_info(info: &[CpInfo], index: u2) -> Result<(), ParseErr> {
if index == 0 { if index == 0 {
return Err(ParseErr("Index must not be 0".to_string())); 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 { match &info[index as usize - 1].inner {
CpInfoInner::$name(class) => Ok(class), CpInfoInner::$name(_) => Ok(()),
kind => Err(ParseErr(format!( kind => Err(ParseErr(format!(
concat!("Expected '", stringify!($name), "', found '{:?}'"), concat!("Expected '", stringify!($name), "', found '{:?}'"),
kind kind
@ -95,40 +143,54 @@ macro_rules! impl_try_from_cp {
}; };
} }
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)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Class { pub struct Class {
/// Entry must be `Utf8` /// Entry must be `Utf8`
pub name_index: FromPool<cp_info::Utf8>, pub name_index: FromPool<Utf8>,
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Fieldref { pub struct Fieldref {
/// May be a class or interface type /// May be a class or interface type
pub class_index: FromPool<cp_info::Class>, pub class_index: FromPool<Class>,
/// Entry must be `NameAndType` /// Entry must be `NameAndType`
pub name_and_type_index: FromPool<cp_info::NameAndType>, pub name_and_type_index: FromPool<NameAndType>,
} }
#[derive(Debug, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct MethodRef { pub struct MethodRef {
/// Must be a class type /// Must be a class type
pub class_index: FromPool<cp_info::Class>, pub class_index: FromPool<Class>,
/// Entry must be `NameAndType` /// Entry must be `NameAndType`
pub name_and_type_index: FromPool<cp_info::NameAndType>, pub name_and_type_index: FromPool<NameAndType>,
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct InterfaceMethodref { pub struct InterfaceMethodref {
/// Must be an interface type /// Must be an interface type
pub class_index: FromPool<cp_info::Class>, pub class_index: FromPool<Class>,
/// Entry must be `NameAndType` /// Entry must be `NameAndType`
pub name_and_type_index: FromPool<cp_info::NameAndType>, pub name_and_type_index: FromPool<NameAndType>,
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct String { pub struct String {
/// Entry must be `Utf8` /// Entry must be `Utf8`
pub string_index: FromPool<cp_info::Utf8>, pub string_index: FromPool<Utf8>,
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
@ -165,9 +227,9 @@ pub struct Double {
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct NameAndType { pub struct NameAndType {
/// Entry must be `Utf8` /// Entry must be `Utf8`
pub name_index: FromPool<cp_info::Utf8>, pub name_index: FromPool<Utf8>,
/// Entry must be `Utf8` /// Entry must be `Utf8`
pub descriptor_index: FromPool<cp_info::Utf8>, pub descriptor_index: FromPool<Utf8>,
} }
#[derive(Debug, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@ -187,15 +249,15 @@ pub struct MethodHandle {
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum MethodHandleIndex { pub enum MethodHandleIndex {
Field(FromPool<cp_info::Fieldref>), Field(FromPool<Fieldref>),
Method(FromPool<cp_info::MethodInfo>), Method(FromPool<MethodRef>),
Interface(FromPool<cp_info::InterfaceMethodref>), Interface(FromPool<InterfaceMethodref>),
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct MethodType { pub struct MethodType {
/// Entry must be `Utf8` /// Entry must be `Utf8`
pub descriptor_index: FromPool<cp_info::Utf8>, pub descriptor_index: FromPool<Utf8>,
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
@ -203,7 +265,7 @@ pub struct InvokeDynamic {
/// Must be a valid index into the `bootstrap_methods` array of the bootstrap method table of this class field /// 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 bootstrap_method_attr_index: u2,
/// Entry must `NameAndType` /// Entry must `NameAndType`
pub name_and_type_index: FromPool<cp_info::NameAndType>, pub name_and_type_index: FromPool<NameAndType>,
} }
// default implementations // default implementations
@ -224,6 +286,21 @@ impl_try_from_cp!(
InvokeDynamic InvokeDynamic
); );
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 // custom implementations
impl<'pool> FromCpInfo<'pool> for Utf8 { impl<'pool> FromCpInfo<'pool> for Utf8 {
type Target = &'pool str; type Target = &'pool str;
@ -235,18 +312,4 @@ impl<'pool> FromCpInfo<'pool> for Utf8 {
_ => unreachable!(), _ => unreachable!(),
} }
} }
#[inline]
fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result<Self::Target, ParseErr> {
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
))),
}
}
} }

View file

@ -4,8 +4,6 @@
//! [The .class specs](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html) //! [The .class specs](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html)
#![allow(dead_code)] #![allow(dead_code)]
use std::marker::PhantomData;
/// All of the Constants in the Constant Pool /// All of the Constants in the Constant Pool
pub mod cp_info; pub mod cp_info;
@ -118,15 +116,6 @@ pub struct AttributeInfo {
pub inner: AttributeInfoInner, pub inner: AttributeInfoInner,
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum ConstantValueIndex {
Long(cp_info::Long),
Float(cp_info::Float),
Double(cp_info::Double),
Integer(cp_info::Integer),
String(cp_info::String),
}
/// The Attributes, without the two common fields /// The Attributes, without the two common fields
#[derive(Debug, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum AttributeInfoInner { pub enum AttributeInfoInner {
@ -138,7 +127,7 @@ pub enum AttributeInfoInner {
/// Only on fields, the constant value of that field /// Only on fields, the constant value of that field
ConstantValue { ConstantValue {
/// Must be of type `Long`/`Float`/`Double`/`Integer`/`String` /// Must be of type `Long`/`Float`/`Double`/`Integer`/`String`
constantvalue_index: FromPool<ConstantValueIndex>, constantvalue_index: FromPool<CpInfoInner>,
}, },
/// Only on methods, contains JVM instructions and auxiliary information for a single method /// Only on methods, contains JVM instructions and auxiliary information for a single method
Code { Code {