Merge remote-tracking branch 'origin/master'

This commit is contained in:
nora 2021-08-28 23:39:06 +02:00
commit 456087553a
5 changed files with 130 additions and 74 deletions

View file

@ -8,4 +8,5 @@ my goal is not to make a fully working jvm that will run your spring application
it would be amazing if it even managed to run a hello world
## what i have for now:
* (Hopefully) working complete `.class` file parser
* Almost working complete `.class` file parser
* Primitive file info for `.class` files similar to `javap`

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 {