mirror of
https://github.com/Noratrieb/coldsquare.git
synced 2026-01-14 08:30:13 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
456087553a
5 changed files with 130 additions and 74 deletions
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()?,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue