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 std::error::Error;
use file_parser::ClassFile;
use std::io;
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;
writeln!(
@ -17,24 +17,21 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
w,
"class {} extends {}{} {{",
&class.this_class.get(cp).name_index.get(cp),
match class.super_class.get(cp) {
match class.super_class.maybe_get(cp) {
None => "<none>",
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::<Result<Vec<_>, ParseErr>>()?
.iter()
.map(|i| i.name_index.get(cp))
.collect::<Result<Vec<_>, ParseErr>>()?
.collect::<Vec<_>>()
.join(",")
)
},
@ -42,7 +39,7 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
writeln!(w, " Attributes:")?;
for attr in &class.attributes {
writeln!(w, " {}", &attr.attribute_name_index.get(cp)?)?;
writeln!(w, " {}", &attr.attribute_name_index.get(cp))?;
}
writeln!(w)?;
@ -51,8 +48,8 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
writeln!(
w,
" {} {}",
&field.descriptor_index.get(cp)?,
&field.name_index.get(cp)?
&field.descriptor_index.get(cp),
&field.name_index.get(cp)
)?;
}
writeln!(w)?;
@ -62,8 +59,8 @@ pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dy
writeln!(
w,
" {} {}",
&method.descriptor_index.get(cp)?,
&method.name_index.get(cp)?,
&method.descriptor_index.get(cp),
&method.name_index.get(cp),
)?;
}

View file

@ -2,7 +2,7 @@ mod model;
#[cfg(test)]
mod test;
use crate::cp_info::FromCpInfo;
use crate::cp_info::ValidateCpInfo;
pub use model::*;
use std::fmt::{Display, Formatter};
@ -46,9 +46,9 @@ impl<'a> Data<'a> {
}
/// 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 _ = 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<T> Parse for FromPool<T> {
impl<T> Parse for FromPool<T>
where
T: ValidateCpInfo,
{
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);
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<Self> {
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<Self> {
fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result<Self> {
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<Self> {
fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result<Self> {
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<Self> {
fn parse(data: &mut Data, _cp: &[CpInfo]) -> Result<Self> {
Ok(Self {
start_pc: data.u2()?,
line_number: data.u2()?,

View file

@ -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<T> {
impl<T: Clone> Copy for FromPool<T> {}
impl<T> From<u2> for FromPool<T> {
#[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<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 {
@ -76,14 +118,20 @@ macro_rules! impl_try_from_cp {
_kind => unreachable!(),
}
}
}
#[inline]
fn try_from_cp_info(info: &'pool [CpInfo], index: u2) -> Result<Self::Target, ParseErr> {
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<cp_info::Utf8>,
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<cp_info::Class>,
pub class_index: FromPool<Class>,
/// 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)]
pub struct MethodRef {
/// Must be a class type
pub class_index: FromPool<cp_info::Class>,
pub class_index: FromPool<Class>,
/// 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)]
pub struct InterfaceMethodref {
/// Must be an interface type
pub class_index: FromPool<cp_info::Class>,
pub class_index: FromPool<Class>,
/// 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)]
pub struct String {
/// Entry must be `Utf8`
pub string_index: FromPool<cp_info::Utf8>,
pub string_index: FromPool<Utf8>,
}
#[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<cp_info::Utf8>,
pub name_index: FromPool<Utf8>,
/// Entry must be `Utf8`
pub descriptor_index: FromPool<cp_info::Utf8>,
pub descriptor_index: FromPool<Utf8>,
}
#[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<cp_info::Fieldref>),
Method(FromPool<cp_info::MethodInfo>),
Interface(FromPool<cp_info::InterfaceMethodref>),
Field(FromPool<Fieldref>),
Method(FromPool<MethodRef>),
Interface(FromPool<InterfaceMethodref>),
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct MethodType {
/// Entry must be `Utf8`
pub descriptor_index: FromPool<cp_info::Utf8>,
pub descriptor_index: FromPool<Utf8>,
}
#[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<cp_info::NameAndType>,
pub name_and_type_index: FromPool<NameAndType>,
}
// 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<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)
#![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<ConstantValueIndex>,
constantvalue_index: FromPool<CpInfoInner>,
},
/// Only on methods, contains JVM instructions and auxiliary information for a single method
Code {