From 125b880f7dbd6a6c2341e9bf7ae93c19d78a146c Mon Sep 17 00:00:00 2001 From: nils <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 26 Aug 2021 16:18:06 +0200 Subject: [PATCH] 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. --- crates/file-info/src/ui.rs | 27 ++--- crates/file-parser/src/lib.rs | 22 ++-- crates/file-parser/src/model/cp_info.rs | 139 +++++++++++++++++------- crates/file-parser/src/model/mod.rs | 13 +-- 4 files changed, 128 insertions(+), 73 deletions(-) diff --git a/crates/file-info/src/ui.rs b/crates/file-info/src/ui.rs index 4ac6629..158e210 100644 --- a/crates/file-info/src/ui.rs +++ b/crates/file-info/src/ui.rs @@ -1,8 +1,8 @@ -use file_parser::{ClassFile, ParseErr}; -use std::error::Error; +use file_parser::ClassFile; +use std::io; use std::io::Write; -pub fn display_class(mut w: W, class: &ClassFile) -> Result<(), Box> { +pub fn display_class(mut w: W, class: &ClassFile) -> Result<(), io::Error> { let cp = &class.constant_pool; writeln!( @@ -17,24 +17,21 @@ pub fn display_class(mut w: W, class: &ClassFile) -> Result<(), Box "", - Some(class) => &class.name_index.get(cp), - } + Some(class) => class.name_index.get(cp), + }, if class.interfaces.is_empty() { "".to_string() } else { format!( " implements {}", - // this is absolutely terrible but it works i guess class .interfaces .iter() .map(|i| i.get(cp)) - .collect::, ParseErr>>()? - .iter() .map(|i| i.name_index.get(cp)) - .collect::, ParseErr>>()? + .collect::>() .join(",") ) }, @@ -42,7 +39,7 @@ pub fn display_class(mut w: W, class: &ClassFile) -> Result<(), Box(mut w: W, class: &ClassFile) -> Result<(), Box(mut w: W, class: &ClassFile) -> Result<(), Box Data<'a> { } /// Parses a u2 and validates it in the constant pool - fn cp<'pool, T: FromCpInfo<'pool>>(&mut self, pool: &'pool [CpInfo]) -> Result> { + fn cp(&mut self, pool: &[CpInfo]) -> Result> { let index = self.u2()?; - let _ = T::try_from_cp_info(pool, index)?; + T::validate_cp_info(pool, index)?; Ok(index.into()) } @@ -117,9 +117,12 @@ macro_rules! parse_primitive { parse_primitive!(u1, u2, u4); -impl Parse for FromPool { +impl Parse for FromPool +where + T: ValidateCpInfo, +{ fn parse(data: &mut Data, cp: &[CpInfo]) -> Result { - Ok(data.u2()?.into()) + data.cp(cp) } } @@ -129,7 +132,9 @@ impl Parse for ClassFile { assert_eq!(magic, 0xCAFEBABE); let minor_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 + dbg!("after constant pool"); let cp = &constant_pool; let access_flags = data.u2()?; let this_class = data.cp(cp)?; @@ -160,6 +165,7 @@ impl Parse for ClassFile { impl Parse for CpInfo { fn parse(data: &mut Data, cp: &[CpInfo]) -> Result { let tag = data.u1()?; + dbg!(tag); Ok(match tag { 7 => Self { @@ -302,7 +308,7 @@ impl Parse for AttributeInfo { } impl Parse for AttributeCodeException { - fn parse(data: &mut Data, cp: &[CpInfo]) -> Result { + fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result { Ok(Self { start_pc: data.last_u2()?, end_pc: data.last_u2()?, @@ -357,7 +363,7 @@ impl Parse for StackMapFrame { } impl Parse for VerificationTypeInfo { - fn parse(data: &mut Data, cp: &[CpInfo]) -> Result { + fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result { let tag = data.u1()?; Ok(match tag { 0 => Self::Top { tag }, @@ -397,7 +403,7 @@ impl Parse for AttributeInnerClass { } impl Parse for AttributeLineNumber { - fn parse(data: &mut Data, cp: &[CpInfo]) -> Result { + fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result { Ok(Self { start_pc: data.u2()?, line_number: data.u2()?, diff --git a/crates/file-parser/src/model/cp_info.rs b/crates/file-parser/src/model/cp_info.rs index 53aa005..101dd03 100644 --- a/crates/file-parser/src/model/cp_info.rs +++ b/crates/file-parser/src/model/cp_info.rs @@ -1,5 +1,5 @@ -use super::*; -use crate::ParseErr; +use crate::{u1, u2, u4, CpInfo, CpInfoInner, ParseErr}; +use std::marker::PhantomData; /// /// An index into the constant pool of the class @@ -18,6 +18,7 @@ pub struct FromPool { impl Copy for FromPool {} impl From for FromPool { + #[inline] fn from(n: u2) -> Self { Self { inner: n, @@ -39,7 +40,7 @@ where { #[inline] 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 { None } 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; fn from_cp_info(info: &'pool CpInfo) -> Self::Target; - fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result; + 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 +where + T: FromCpInfo<'pool>, +{ + type Target = Option; + + #[inline] + fn from_cp_info(_info: &'pool CpInfo) -> Self::Target { + unreachable!("FromPool> 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 ValidateCpInfo for Option +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 { @@ -76,14 +118,20 @@ macro_rules! impl_try_from_cp { _kind => unreachable!(), } } + } - #[inline] - fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result { + 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(class) => Ok(class), + CpInfoInner::$name(_) => Ok(()), kind => Err(ParseErr(format!( concat!("Expected '", stringify!($name), "', found '{:?}'"), 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)] pub struct Class { /// Entry must be `Utf8` - pub name_index: FromPool, + 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, + pub class_index: FromPool, /// Entry must be `NameAndType` - pub name_and_type_index: FromPool, + pub name_and_type_index: FromPool, } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct MethodRef { /// Must be a class type - pub class_index: FromPool, + pub class_index: FromPool, /// Entry must be `NameAndType` - pub name_and_type_index: FromPool, + 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, + pub class_index: FromPool, /// Entry must be `NameAndType` - pub name_and_type_index: FromPool, + pub name_and_type_index: FromPool, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct String { /// Entry must be `Utf8` - pub string_index: FromPool, + pub string_index: FromPool, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -165,9 +227,9 @@ pub struct Double { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct NameAndType { /// Entry must be `Utf8` - pub name_index: FromPool, + pub name_index: FromPool, /// Entry must be `Utf8` - pub descriptor_index: FromPool, + pub descriptor_index: FromPool, } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -187,15 +249,15 @@ pub struct MethodHandle { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum MethodHandleIndex { - Field(FromPool), - Method(FromPool), - Interface(FromPool), + Field(FromPool), + Method(FromPool), + Interface(FromPool), } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct MethodType { /// Entry must be `Utf8` - pub descriptor_index: FromPool, + pub descriptor_index: FromPool, } #[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 pub bootstrap_method_attr_index: u2, /// Entry must `NameAndType` - pub name_and_type_index: FromPool, + pub name_and_type_index: FromPool, } // default implementations @@ -224,6 +286,21 @@ impl_try_from_cp!( 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 impl<'pool> FromCpInfo<'pool> for Utf8 { type Target = &'pool str; @@ -235,18 +312,4 @@ impl<'pool> FromCpInfo<'pool> for Utf8 { _ => 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 - ))), - } - } } diff --git a/crates/file-parser/src/model/mod.rs b/crates/file-parser/src/model/mod.rs index 3b5148e..73b3c7d 100644 --- a/crates/file-parser/src/model/mod.rs +++ b/crates/file-parser/src/model/mod.rs @@ -4,8 +4,6 @@ //! [The .class specs](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html) #![allow(dead_code)] -use std::marker::PhantomData; - /// All of the Constants in the Constant Pool pub mod cp_info; @@ -118,15 +116,6 @@ pub struct AttributeInfo { 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 #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum AttributeInfoInner { @@ -138,7 +127,7 @@ pub enum AttributeInfoInner { /// Only on fields, the constant value of that field ConstantValue { /// Must be of type `Long`/`Float`/`Double`/`Integer`/`String` - constantvalue_index: FromPool, + constantvalue_index: FromPool, }, /// Only on methods, contains JVM instructions and auxiliary information for a single method Code {