mirror of
https://github.com/Noratrieb/coldsquare.git
synced 2026-01-16 09:25:08 +01:00
changed a lot of stuff
This commit is contained in:
parent
860346a910
commit
40f1cbfab6
8 changed files with 402 additions and 266 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::parse::{ClassFile, ParseErr};
|
use crate::parse::{ClassFile, ParseErr};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
mod execute;
|
mod execute;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
@ -8,7 +9,7 @@ pub fn parse_class_file(file: &[u8]) -> Result<ClassFile, ParseErr> {
|
||||||
parse::parse_class_file(file)
|
parse::parse_class_file(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_class<W>(w: W, class: &ClassFile) -> std::io::Result<()>
|
pub fn display_class<W>(w: W, class: &ClassFile) -> Result<(), Box<dyn Error>>
|
||||||
where
|
where
|
||||||
W: std::io::Write,
|
W: std::io::Write,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
115
src/parse/mod.rs
115
src/parse/mod.rs
|
|
@ -44,7 +44,7 @@ impl<'a> Data<'a> {
|
||||||
Ok(((self.u1()? as u2) << 8) | self.u1()? as u2)
|
Ok(((self.u1()? as u2) << 8) | self.u1()? as u2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cp(&mut self) -> Result<FromPool> {
|
fn cp<T>(&mut self) -> Result<FromPool<T>> {
|
||||||
Ok(self.u2()?.into())
|
Ok(self.u2()?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ trait Parse {
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_vec<T: Parse, S: Into<usize>>(data: &mut Data, len: S) -> Result<Vec<T>> {
|
fn parse_vec<T: Parse, S: Into<usize>>(len: S, data: &mut Data) -> Result<Vec<T>> {
|
||||||
let len = len.into();
|
let len = len.into();
|
||||||
let mut vec = Vec::with_capacity(len);
|
let mut vec = Vec::with_capacity(len);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
|
|
@ -113,7 +113,7 @@ macro_rules! parse_primitive {
|
||||||
|
|
||||||
parse_primitive!(u1, u2, u4);
|
parse_primitive!(u1, u2, u4);
|
||||||
|
|
||||||
impl Parse for FromPool {
|
impl<T> Parse for FromPool<T> {
|
||||||
fn parse(data: &mut Data) -> Result<Self> {
|
fn parse(data: &mut Data) -> Result<Self> {
|
||||||
Ok(data.u2()?.into())
|
Ok(data.u2()?.into())
|
||||||
}
|
}
|
||||||
|
|
@ -125,36 +125,26 @@ 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()?;
|
||||||
let constant_pool_count = data.u2()?;
|
let constant_pool = parse_vec(data.u2()? - 1, data)?; // the minus one is important
|
||||||
let constant_pool = parse_vec(data, constant_pool_count - 1)?; // the minus one is important
|
|
||||||
let access_flags = data.u2()?;
|
let access_flags = data.u2()?;
|
||||||
let this_class = data.cp()?;
|
let this_class = data.cp()?;
|
||||||
let super_class = data.cp()?;
|
let super_class = data.cp()?;
|
||||||
let interfaces_count = data.u2()?;
|
let interfaces = parse_vec(data.u2()?, data)?;
|
||||||
let interfaces = parse_vec(data, interfaces_count)?;
|
let fields = parse_vec(data.u2()?, data)?;
|
||||||
let fields_count = data.u2()?;
|
let methods = parse_vec(data.u2()?, data)?;
|
||||||
let fields = parse_vec(data, fields_count)?;
|
let attributes = parse_vec(data.u2()?, data)?;
|
||||||
let method_count = data.u2()?;
|
|
||||||
let methods = parse_vec(data, method_count)?;
|
|
||||||
let attributes_count = data.u2()?;
|
|
||||||
let attributes = parse_vec(data, attributes_count)?;
|
|
||||||
|
|
||||||
let mut class = Self {
|
let mut class = Self {
|
||||||
magic,
|
magic,
|
||||||
minor_version,
|
minor_version,
|
||||||
major_version,
|
major_version,
|
||||||
constant_pool_count,
|
|
||||||
constant_pool,
|
constant_pool,
|
||||||
access_flags,
|
access_flags,
|
||||||
this_class,
|
this_class,
|
||||||
super_class,
|
super_class,
|
||||||
interfaces_count,
|
|
||||||
interfaces,
|
interfaces,
|
||||||
fields_count,
|
|
||||||
fields,
|
fields,
|
||||||
method_count,
|
|
||||||
methods,
|
methods,
|
||||||
attributes_count,
|
|
||||||
attributes,
|
attributes,
|
||||||
};
|
};
|
||||||
resolve_attributes(&mut class)?;
|
resolve_attributes(&mut class)?;
|
||||||
|
|
@ -232,8 +222,7 @@ impl Parse for CpInfo {
|
||||||
1 => Self {
|
1 => Self {
|
||||||
tag,
|
tag,
|
||||||
inner: CpInfoInner::Utf8(cp_info::Utf8 {
|
inner: CpInfoInner::Utf8(cp_info::Utf8 {
|
||||||
length: data.u2()?,
|
bytes: String::from_utf8(parse_vec(data.u2()?, data)?).map_err(|err| {
|
||||||
bytes: String::from_utf8(parse_vec(data, data.last_u2()?)?).map_err(|err| {
|
|
||||||
ParseErr(format!("Invalid utf8 in CpInfo::Utf8: {}", err))
|
ParseErr(format!("Invalid utf8 in CpInfo::Utf8: {}", err))
|
||||||
})?,
|
})?,
|
||||||
}),
|
}),
|
||||||
|
|
@ -242,7 +231,17 @@ impl Parse for CpInfo {
|
||||||
tag,
|
tag,
|
||||||
inner: CpInfoInner::MethodHandle(cp_info::MethodHandle {
|
inner: CpInfoInner::MethodHandle(cp_info::MethodHandle {
|
||||||
reference_kind: data.u1()?,
|
reference_kind: data.u1()?,
|
||||||
reference_index: data.cp()?,
|
reference_index: match data.last_u1()? {
|
||||||
|
1..=4 => cp_info::MethodHandleIndex::Field(data.cp()?),
|
||||||
|
5..=8 => cp_info::MethodHandleIndex::Method(data.cp()?),
|
||||||
|
9 => cp_info::MethodHandleIndex::Interface(data.cp()?),
|
||||||
|
n => {
|
||||||
|
return Err(ParseErr(format!(
|
||||||
|
"Invalid MethodHandle reference kind: {}",
|
||||||
|
n
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
16 => Self {
|
16 => Self {
|
||||||
|
|
@ -269,8 +268,7 @@ impl Parse for FieldInfo {
|
||||||
access_flags: data.u2()?,
|
access_flags: data.u2()?,
|
||||||
name_index: data.cp()?,
|
name_index: data.cp()?,
|
||||||
descriptor_index: data.cp()?,
|
descriptor_index: data.cp()?,
|
||||||
attributes_count: data.u2()?,
|
attributes: parse_vec(data.u2()?, data)?,
|
||||||
attributes: parse_vec(data, data.last_u2()?)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,8 +279,7 @@ impl Parse for MethodInfo {
|
||||||
access_flags: data.u2()?,
|
access_flags: data.u2()?,
|
||||||
name_index: data.cp()?,
|
name_index: data.cp()?,
|
||||||
descriptor_index: data.cp()?,
|
descriptor_index: data.cp()?,
|
||||||
attributes_count: data.u2()?,
|
attributes: parse_vec(data.u2()?, data)?,
|
||||||
attributes: parse_vec(data, data.last_u2()?)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +290,7 @@ impl Parse for AttributeInfo {
|
||||||
attribute_name_index: data.cp()?,
|
attribute_name_index: data.cp()?,
|
||||||
attribute_length: data.u4()?,
|
attribute_length: data.u4()?,
|
||||||
inner: AttributeInfoInner::Unknown {
|
inner: AttributeInfoInner::Unknown {
|
||||||
attribute_content: parse_vec(data, data.last_u4()? as usize)?,
|
attribute_content: parse_vec(data.last_u4()? as usize, data)?,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -336,15 +333,13 @@ impl Parse for StackMapFrame {
|
||||||
252..=254 => Self::AppendFrame {
|
252..=254 => Self::AppendFrame {
|
||||||
frame_type,
|
frame_type,
|
||||||
offset_delta: data.u2()?,
|
offset_delta: data.u2()?,
|
||||||
locals: parse_vec(data, data.last_u2()?)?,
|
locals: parse_vec(data.last_u2()?, data)?,
|
||||||
},
|
},
|
||||||
255 => Self::FullFrame {
|
255 => Self::FullFrame {
|
||||||
frame_type,
|
frame_type,
|
||||||
offset_delta: data.u2()?,
|
offset_delta: data.u2()?,
|
||||||
number_of_locals: data.u2()?,
|
locals: parse_vec(data.u2()?, data)?,
|
||||||
locals: parse_vec(data, data.last_u2()?)?,
|
stack: parse_vec(data.u2()?, data)?,
|
||||||
number_of_stack_items: data.u2()?,
|
|
||||||
stack: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
_ => Err(ParseErr(format!(
|
_ => Err(ParseErr(format!(
|
||||||
"Invalid StackMapFrame type: {}",
|
"Invalid StackMapFrame type: {}",
|
||||||
|
|
@ -367,7 +362,7 @@ impl Parse for VerificationTypeInfo {
|
||||||
6 => Self::UninitializedThis { tag },
|
6 => Self::UninitializedThis { tag },
|
||||||
7 => Self::Object {
|
7 => Self::Object {
|
||||||
tag,
|
tag,
|
||||||
cpool_index: data.u2()?,
|
cpool_index: data.u2()?.into(),
|
||||||
},
|
},
|
||||||
8 => Self::Uninitialized {
|
8 => Self::Uninitialized {
|
||||||
tag,
|
tag,
|
||||||
|
|
@ -418,7 +413,7 @@ impl Parse for Annotation {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
type_index: data.cp()?,
|
type_index: data.cp()?,
|
||||||
num_element_value_pairs: data.u2()?,
|
num_element_value_pairs: data.u2()?,
|
||||||
element_value_pairs: parse_vec(data, data.last_u2()?)?,
|
element_value_pairs: parse_vec(data.last_u2()?, data)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -457,8 +452,7 @@ impl Parse for AnnotationElementValueValue {
|
||||||
annotation: Box::new(Annotation::parse(data)?),
|
annotation: Box::new(Annotation::parse(data)?),
|
||||||
},
|
},
|
||||||
'[' => Self::ArrayValue {
|
'[' => Self::ArrayValue {
|
||||||
num_values: data.u2()?,
|
values: parse_vec(data.u2()?, data)?,
|
||||||
values: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
_ => Err(ParseErr(format!(
|
_ => Err(ParseErr(format!(
|
||||||
"Invalid AnnotationElementValueValue tag: {}",
|
"Invalid AnnotationElementValueValue tag: {}",
|
||||||
|
|
@ -471,8 +465,7 @@ impl Parse for AnnotationElementValueValue {
|
||||||
impl Parse for ParameterAnnotation {
|
impl Parse for ParameterAnnotation {
|
||||||
fn parse(data: &mut Data) -> Result<Self> {
|
fn parse(data: &mut Data) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
num_annotations: data.u2()?,
|
annotations: parse_vec(data.u2()?, data)?,
|
||||||
annotations: parse_vec(data, data.last_u2()?)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -481,8 +474,7 @@ impl Parse for BootstrapMethod {
|
||||||
fn parse(data: &mut Data) -> Result<Self> {
|
fn parse(data: &mut Data) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
bootstrap_method_ref: data.cp()?,
|
bootstrap_method_ref: data.cp()?,
|
||||||
num_bootstrap_arguments: data.u2()?,
|
bootstrap_arguments: parse_vec(data.u2()?, data)?,
|
||||||
bootstrap_arguments: parse_vec(data, data.last_u2()?)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -558,7 +550,7 @@ impl AttributeInfo {
|
||||||
|
|
||||||
fn resolve_attribute_inner(
|
fn resolve_attribute_inner(
|
||||||
&mut self,
|
&mut self,
|
||||||
attribute_name_index: FromPool,
|
attribute_name_index: FromPool<cp_info::Utf8>,
|
||||||
attribute_length: u32,
|
attribute_length: u32,
|
||||||
name: &str,
|
name: &str,
|
||||||
data: &mut Data,
|
data: &mut Data,
|
||||||
|
|
@ -581,12 +573,9 @@ impl AttributeInfo {
|
||||||
inner: AttributeInfoInner::Code {
|
inner: AttributeInfoInner::Code {
|
||||||
max_stack: data.u2()?,
|
max_stack: data.u2()?,
|
||||||
max_locals: data.u2()?,
|
max_locals: data.u2()?,
|
||||||
code_length: data.u4()?,
|
code: parse_vec(data.u4()? as usize, data)?,
|
||||||
code: parse_vec(data, data.last_u4()? as usize)?,
|
exception_table: parse_vec(data.u2()?, data)?,
|
||||||
exception_table_length: data.u2()?,
|
attributes: parse_vec(data.u2()?, data)?,
|
||||||
exception_table: parse_vec(data, data.last_u2()?)?,
|
|
||||||
attributes_count: data.u2()?,
|
|
||||||
attributes: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if let AttributeInfoInner::Code {
|
if let AttributeInfoInner::Code {
|
||||||
|
|
@ -607,23 +596,21 @@ impl AttributeInfo {
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::StackMapTable {
|
inner: AttributeInfoInner::StackMapTable {
|
||||||
number_of_entries: data.u2()?,
|
number_of_entries: data.u2()?,
|
||||||
entries: parse_vec(data, data.last_u2()?)?,
|
entries: parse_vec(data.last_u2()?, data)?,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Exceptions" => Self {
|
"Exceptions" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::Exceptions {
|
inner: AttributeInfoInner::Exceptions {
|
||||||
number_of_exceptions: data.u2()?,
|
exception_index_table: parse_vec(data.u2()?, data)?,
|
||||||
exception_index_table: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"InnerClasses" => Self {
|
"InnerClasses" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::InnerClasses {
|
inner: AttributeInfoInner::InnerClasses {
|
||||||
number_of_classes: data.u2()?,
|
classes: parse_vec(data.u2()?, data)?,
|
||||||
classes: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"EnclosingMethod" => Self {
|
"EnclosingMethod" => Self {
|
||||||
|
|
@ -657,31 +644,28 @@ impl AttributeInfo {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::SourceDebugExtension {
|
inner: AttributeInfoInner::SourceDebugExtension {
|
||||||
debug_extension: parse_vec(data, data.last_u2()?)?,
|
debug_extension: parse_vec(data.last_u2()?, data)?,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"LineNumberTable" => Self {
|
"LineNumberTable" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::LineNumberTable {
|
inner: AttributeInfoInner::LineNumberTable {
|
||||||
line_number_table_length: data.u2()?,
|
line_number_table: parse_vec(data.u2()?, data)?,
|
||||||
line_number_table: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"LocalVariableTable" => Self {
|
"LocalVariableTable" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::LocalVariableTable {
|
inner: AttributeInfoInner::LocalVariableTable {
|
||||||
local_variable_table_length: data.u2()?,
|
local_variable_table: parse_vec(data.u2()?, data)?,
|
||||||
local_variable_table: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"LocalVariableTypeTable" => Self {
|
"LocalVariableTypeTable" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::LocalVariableTypeTable {
|
inner: AttributeInfoInner::LocalVariableTypeTable {
|
||||||
local_variable_table_length: data.u2()?,
|
local_variable_table: parse_vec(data.u2()?, data)?,
|
||||||
local_variable_table: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Deprecated" => Self {
|
"Deprecated" => Self {
|
||||||
|
|
@ -693,32 +677,28 @@ impl AttributeInfo {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::RuntimeVisibleAnnotations {
|
inner: AttributeInfoInner::RuntimeVisibleAnnotations {
|
||||||
num_annotations: data.u2()?,
|
annotations: parse_vec(data.u2()?, data)?,
|
||||||
annotations: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"RuntimeInvisibleAnnotations" => Self {
|
"RuntimeInvisibleAnnotations" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::RuntimeInvisibleAnnotations {
|
inner: AttributeInfoInner::RuntimeInvisibleAnnotations {
|
||||||
num_annotations: data.u2()?,
|
annotations: parse_vec(data.u2()?, data)?,
|
||||||
annotations: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"RuntimeVisibleParameterAnnotations" => Self {
|
"RuntimeVisibleParameterAnnotations" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::RuntimeVisibleParameterAnnotations {
|
inner: AttributeInfoInner::RuntimeVisibleParameterAnnotations {
|
||||||
num_parameters: data.u1()?,
|
parameter_annotations: parse_vec(data.u1()?, data)?,
|
||||||
parameter_annotations: parse_vec(data, data.last_u1()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"RuntimeInvisibleParameterAnnotations" => Self {
|
"RuntimeInvisibleParameterAnnotations" => Self {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::RuntimeInvisibleParameterAnnotations {
|
inner: AttributeInfoInner::RuntimeInvisibleParameterAnnotations {
|
||||||
num_parameters: data.u1()?,
|
parameter_annotations: parse_vec(data.u1()?, data)?,
|
||||||
parameter_annotations: parse_vec(data, data.last_u1()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"AnnotationDefault" => Self {
|
"AnnotationDefault" => Self {
|
||||||
|
|
@ -735,8 +715,7 @@ impl AttributeInfo {
|
||||||
attribute_name_index,
|
attribute_name_index,
|
||||||
attribute_length,
|
attribute_length,
|
||||||
inner: AttributeInfoInner::BootstrapMethods {
|
inner: AttributeInfoInner::BootstrapMethods {
|
||||||
num_bootstrap_methods: data.u2()?,
|
bootstrap_methods: parse_vec(data.u2()?, data)?,
|
||||||
bootstrap_methods: parse_vec(data, data.last_u2()?)?,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
name => return Err(ParseErr(format!("Invalid Attribute name: {}", name))),
|
name => return Err(ParseErr(format!("Invalid Attribute name: {}", name))),
|
||||||
|
|
|
||||||
221
src/parse/model/cp_info.rs
Normal file
221
src/parse/model/cp_info.rs
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// An index into the constant pool of the class
|
||||||
|
/// `T` -> What type the target value is supposed to be. Create an enum if multiple values can be there
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub struct FromPool<T> {
|
||||||
|
inner: u2,
|
||||||
|
_marker: PhantomData<fn() -> T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// could probably be derived if I chose a better marker
|
||||||
|
impl<T: Clone> Copy for FromPool<T> {}
|
||||||
|
|
||||||
|
impl<T> std::ops::Deref for FromPool<T> {
|
||||||
|
type Target = u2;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<u2> for FromPool<T> {
|
||||||
|
fn from(n: u2) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: n,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromCpInfo<'a> {
|
||||||
|
type Target;
|
||||||
|
fn from_cp_info(info: &'a CpInfo) -> Result<Self::Target, ParseErr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> FromCpInfo<'a> for Option<T>
|
||||||
|
where
|
||||||
|
T: FromCpInfo<'a, Target = &'a T>,
|
||||||
|
T: 'a,
|
||||||
|
{
|
||||||
|
type Target = Option<&'a T>;
|
||||||
|
|
||||||
|
fn from_cp_info(info: &'a CpInfo) -> Result<Self::Target, ParseErr> {
|
||||||
|
Ok(T::from_cp_info(info).ok())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> FromPool<T>
|
||||||
|
where
|
||||||
|
T: FromCpInfo<'a>,
|
||||||
|
{
|
||||||
|
pub fn get(&self, pool: &'a [CpInfo]) -> Result<T::Target, ParseErr> {
|
||||||
|
T::from_cp_info(&pool[self.inner as usize - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_try_from_cp {
|
||||||
|
($($name:ident),*) => {
|
||||||
|
$(
|
||||||
|
impl<'a> FromCpInfo<'a> for $name {
|
||||||
|
type Target = &'a Self;
|
||||||
|
|
||||||
|
fn from_cp_info(info: &'a CpInfo) -> Result<Self::Target, ParseErr> {
|
||||||
|
match &info.inner {
|
||||||
|
CpInfoInner::$name(class) => Ok(class),
|
||||||
|
kind => Err(ParseErr(format!(
|
||||||
|
concat!("Expected '", stringify!($name), "', found '{:?}'"),
|
||||||
|
kind
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Class {
|
||||||
|
/// Entry must be `Utf8`
|
||||||
|
pub name_index: FromPool<cp_info::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>,
|
||||||
|
/// Entry must be `NameAndType`
|
||||||
|
pub name_and_type_index: FromPool<cp_info::NameAndType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct MethodRef {
|
||||||
|
/// Must be a class type
|
||||||
|
pub class_index: FromPool<cp_info::Class>,
|
||||||
|
/// Entry must be `NameAndType`
|
||||||
|
pub name_and_type_index: FromPool<cp_info::NameAndType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct InterfaceMethodref {
|
||||||
|
/// Must be an interface type
|
||||||
|
pub class_index: FromPool<cp_info::Class>,
|
||||||
|
/// Entry must be `NameAndType`
|
||||||
|
pub name_and_type_index: FromPool<cp_info::NameAndType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct String {
|
||||||
|
/// Entry must be `Utf8`
|
||||||
|
pub string_index: FromPool<cp_info::Utf8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Integer {
|
||||||
|
// Big endian
|
||||||
|
pub bytes: u4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Float {
|
||||||
|
/// IEEE 754 floating-point single format, big endian
|
||||||
|
pub bytes: u4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 8 byte constants take up two spaces in the constant pool
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Long {
|
||||||
|
/// Big endian
|
||||||
|
pub high_bytes: u4,
|
||||||
|
/// Big endian
|
||||||
|
pub low_bytes: u4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 8 byte constants take up two spaces in the constant pool
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Double {
|
||||||
|
/// IEEE 754 floating-point double format, big endian
|
||||||
|
pub high_bytes: u4,
|
||||||
|
/// IEEE 754 floating-point double format, big endian
|
||||||
|
pub low_bytes: u4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Any field or method, without the class it belongs to
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct NameAndType {
|
||||||
|
/// Entry must be `Utf8`
|
||||||
|
pub name_index: FromPool<cp_info::Utf8>,
|
||||||
|
/// Entry must be `Utf8`
|
||||||
|
pub descriptor_index: FromPool<cp_info::Utf8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Utf8 {
|
||||||
|
/// Contains modified UTF-8
|
||||||
|
pub bytes: std::string::String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct MethodHandle {
|
||||||
|
/// The kind of method handle (0-9)
|
||||||
|
/// If the kind is 1-4, the entry must be `FieldRef`. If the kind is 5-8, the entry must be `MethodRef`
|
||||||
|
/// If the kind is 9, the entry must be `InterfaceMethodRef`
|
||||||
|
pub reference_kind: u1,
|
||||||
|
pub reference_index: MethodHandleIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct MethodType {
|
||||||
|
/// Entry must be `Utf8`
|
||||||
|
pub descriptor_index: FromPool<cp_info::Utf8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// default implementations
|
||||||
|
|
||||||
|
impl_try_from_cp!(
|
||||||
|
Class,
|
||||||
|
Fieldref,
|
||||||
|
MethodRef,
|
||||||
|
InterfaceMethodref,
|
||||||
|
String,
|
||||||
|
Integer,
|
||||||
|
Float,
|
||||||
|
Long,
|
||||||
|
Double,
|
||||||
|
NameAndType,
|
||||||
|
MethodHandle,
|
||||||
|
MethodType,
|
||||||
|
InvokeDynamic
|
||||||
|
);
|
||||||
|
|
||||||
|
// custom implementations
|
||||||
|
impl<'a> FromCpInfo<'a> for Utf8 {
|
||||||
|
type Target = &'a str;
|
||||||
|
|
||||||
|
fn from_cp_info(info: &'a CpInfo) -> Result<Self::Target, ParseErr> {
|
||||||
|
match &info.inner {
|
||||||
|
CpInfoInner::Utf8(class) => Ok(&class.bytes),
|
||||||
|
kind => Err(ParseErr(format!(
|
||||||
|
concat!("Expected '", stringify!($name), "', found '{:?}'"),
|
||||||
|
kind
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,14 @@
|
||||||
//! [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 crate::parse::ParseErr;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
/// All of the Constants in the Constant Pool
|
||||||
|
pub mod cp_info;
|
||||||
|
|
||||||
|
pub use cp_info::FromPool;
|
||||||
|
|
||||||
// The types used in the specs
|
// The types used in the specs
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub type u1 = u8;
|
pub type u1 = u8;
|
||||||
|
|
@ -12,32 +20,6 @@ pub type u2 = u16;
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub type u4 = u32;
|
pub type u4 = u32;
|
||||||
|
|
||||||
///
|
|
||||||
/// An index into the constant pool of the class
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
||||||
pub struct FromPool(u2);
|
|
||||||
|
|
||||||
impl std::ops::Deref for FromPool {
|
|
||||||
type Target = u2;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u2> for FromPool {
|
|
||||||
fn from(n: u2) -> Self {
|
|
||||||
Self(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromPool {
|
|
||||||
pub fn get<'a>(&self, pool: &'a [CpInfo]) -> &'a CpInfo {
|
|
||||||
&pool[self.0 as usize - 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// # Represents a .class file
|
/// # Represents a .class file
|
||||||
///
|
///
|
||||||
|
|
@ -49,30 +31,21 @@ pub struct ClassFile {
|
||||||
pub minor_version: u2,
|
pub minor_version: u2,
|
||||||
/// The version of the class file (X.)
|
/// The version of the class file (X.)
|
||||||
pub major_version: u2,
|
pub major_version: u2,
|
||||||
/// Number of entries in the constant pool + 1
|
/// `constant_pool_count` = Number of entries in the constant pool + 1
|
||||||
pub constant_pool_count: u2,
|
|
||||||
/// The constant pool. Indexed from 1 to constant_pool_count - 1
|
/// The constant pool. Indexed from 1 to constant_pool_count - 1
|
||||||
pub constant_pool: Vec<CpInfo>,
|
pub constant_pool: Vec<CpInfo>,
|
||||||
/// Mask of `ClassAccessFlag` used to denote access permissions
|
/// Mask of `ClassAccessFlag` used to denote access permissions
|
||||||
pub access_flags: u2,
|
pub access_flags: u2,
|
||||||
/// A valid index into the `constant_pool` table. The entry must be a `ConstantClassInfo`
|
/// A valid index into the `constant_pool` table. The entry must be a `Class`
|
||||||
pub this_class: FromPool,
|
pub this_class: FromPool<cp_info::Class>,
|
||||||
/// Zero or a valid index into the `constant_pool` table
|
/// Zero or a valid index into the `constant_pool` table
|
||||||
pub super_class: FromPool,
|
pub super_class: FromPool<Option<cp_info::Class>>,
|
||||||
/// The number if direct superinterfaces of this class or interface type
|
/// Each entry must be a valid index into the `constant_pool` table. The entry must be a `Class`
|
||||||
pub interfaces_count: u2,
|
pub interfaces: Vec<FromPool<cp_info::Class>>,
|
||||||
/// Each entry must be a valid index into the `constant_pool` table. The entry must be a `ConstantClassInfo`
|
|
||||||
pub interfaces: Vec<u2>,
|
|
||||||
/// The number of fields in the `fields` table
|
|
||||||
pub fields_count: u2,
|
|
||||||
/// All fields of the class. Contains only fields of the class itself
|
/// All fields of the class. Contains only fields of the class itself
|
||||||
pub fields: Vec<FieldInfo>,
|
pub fields: Vec<FieldInfo>,
|
||||||
/// The number of methods in `methods`
|
|
||||||
pub method_count: u2,
|
|
||||||
/// All methods of the class. If it's neither Native nor Abstract, the implementation has to be provided too
|
/// All methods of the class. If it's neither Native nor Abstract, the implementation has to be provided too
|
||||||
pub methods: Vec<MethodInfo>,
|
pub methods: Vec<MethodInfo>,
|
||||||
/// The number of attributes in `attributes`
|
|
||||||
pub attributes_count: u2,
|
|
||||||
/// All attributes of the class
|
/// All attributes of the class
|
||||||
pub attributes: Vec<AttributeInfo>,
|
pub attributes: Vec<AttributeInfo>,
|
||||||
}
|
}
|
||||||
|
|
@ -107,110 +80,15 @@ pub enum CpInfoInner {
|
||||||
InvokeDynamic(cp_info::InvokeDynamic),
|
InvokeDynamic(cp_info::InvokeDynamic),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod cp_info {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Class {
|
|
||||||
/// Entry must be `Utf8`
|
|
||||||
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,
|
|
||||||
/// Entry must be `NameAndType`
|
|
||||||
pub name_and_type_index: FromPool,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct MethodRef {
|
|
||||||
/// Must be a class type
|
|
||||||
pub class_index: FromPool,
|
|
||||||
/// Entry must be `NameAndType`
|
|
||||||
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,
|
|
||||||
/// Entry must be `NameAndType`
|
|
||||||
pub name_and_type_index: FromPool,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct String {
|
|
||||||
/// Entry must be `Utf8`
|
|
||||||
pub string_index: FromPool,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Integer {
|
|
||||||
// Big endian
|
|
||||||
pub bytes: u4,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Float {
|
|
||||||
/// IEEE 754 floating-point single format, big endian
|
|
||||||
pub bytes: u4,
|
|
||||||
}
|
|
||||||
/// 8 byte constants take up two spaces in the constant pool
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Long {
|
|
||||||
/// Big endian
|
|
||||||
pub high_bytes: u4,
|
|
||||||
/// Big endian
|
|
||||||
pub low_bytes: u4,
|
|
||||||
}
|
|
||||||
/// 8 byte constants take up two spaces in the constant pool
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Double {
|
|
||||||
/// IEEE 754 floating-point double format, big endian
|
|
||||||
pub high_bytes: u4,
|
|
||||||
/// IEEE 754 floating-point double format, big endian
|
|
||||||
pub low_bytes: u4,
|
|
||||||
}
|
|
||||||
/// Any field or method, without the class it belongs to
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct NameAndType {
|
|
||||||
/// Entry must be `Utf8`
|
|
||||||
pub name_index: FromPool,
|
|
||||||
/// Entry must be `Utf8`
|
|
||||||
pub descriptor_index: FromPool,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct Utf8 {
|
|
||||||
/// The length of the String. Not null-terminated.
|
|
||||||
pub length: u2,
|
|
||||||
/// Contains modified UTF-8
|
|
||||||
pub bytes: std::string::String,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct MethodHandle {
|
|
||||||
/// The kind of method handle (0-9)
|
|
||||||
pub reference_kind: u1,
|
|
||||||
/// If the kind is 1-4, the entry must be `FieldRef`. If the kind is 5-8, the entry must be `MethodRef`
|
|
||||||
/// If the kind is 9, the entry must be `InterfaceMethodRef`
|
|
||||||
pub reference_index: FromPool,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct MethodType {
|
|
||||||
/// Entry must be `Utf8`
|
|
||||||
pub descriptor_index: FromPool,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about a field
|
/// Information about a field
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct FieldInfo {
|
pub struct FieldInfo {
|
||||||
|
/// Mask of `FieldAccessFlag` used to denote access permissions
|
||||||
pub access_flags: u2,
|
pub access_flags: u2,
|
||||||
pub name_index: FromPool,
|
/// Entry must be `Utf8`
|
||||||
pub descriptor_index: FromPool,
|
pub name_index: FromPool<cp_info::Utf8>,
|
||||||
pub attributes_count: u2,
|
/// Entry must be `Utf8`
|
||||||
|
pub descriptor_index: FromPool<cp_info::Utf8>,
|
||||||
pub attributes: Vec<AttributeInfo>,
|
pub attributes: Vec<AttributeInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,11 +98,9 @@ pub struct MethodInfo {
|
||||||
/// Mask of `MethodAccessFlag` used to denote access permissions
|
/// Mask of `MethodAccessFlag` used to denote access permissions
|
||||||
pub access_flags: u2,
|
pub access_flags: u2,
|
||||||
/// Index to the `constant_pool` of the method name, must be `Utf8`
|
/// Index to the `constant_pool` of the method name, must be `Utf8`
|
||||||
pub name_index: FromPool,
|
pub name_index: FromPool<cp_info::Utf8>,
|
||||||
/// Index to the `constant_pool` of the method descriptor, must be `Utf8`
|
/// Index to the `constant_pool` of the method descriptor, must be `Utf8`
|
||||||
pub descriptor_index: FromPool,
|
pub descriptor_index: FromPool<cp_info::Utf8>,
|
||||||
/// The amount of attributes for this method
|
|
||||||
pub attributes_count: u2,
|
|
||||||
/// The attributes for this method
|
/// The attributes for this method
|
||||||
pub attributes: Vec<AttributeInfo>,
|
pub attributes: Vec<AttributeInfo>,
|
||||||
}
|
}
|
||||||
|
|
@ -237,12 +113,21 @@ pub struct MethodInfo {
|
||||||
/// _index: Index to the `constant_pool` table of any type
|
/// _index: Index to the `constant_pool` table of any type
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct AttributeInfo {
|
pub struct AttributeInfo {
|
||||||
pub attribute_name_index: FromPool,
|
pub attribute_name_index: FromPool<cp_info::Utf8>,
|
||||||
pub attribute_length: u4,
|
pub attribute_length: u4,
|
||||||
/// The attribute value
|
/// The attribute value
|
||||||
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 {
|
||||||
|
|
@ -254,7 +139,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,
|
constantvalue_index: FromPool<ConstantValueIndex>,
|
||||||
},
|
},
|
||||||
/// 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 {
|
||||||
|
|
@ -262,15 +147,10 @@ pub enum AttributeInfoInner {
|
||||||
max_stack: u2,
|
max_stack: u2,
|
||||||
/// The number of the local variables array, including the parameters
|
/// The number of the local variables array, including the parameters
|
||||||
max_locals: u2,
|
max_locals: u2,
|
||||||
/// The length of the JVM bytecode in bytes
|
|
||||||
code_length: u4,
|
|
||||||
/// The JVM bytecode of this method
|
/// The JVM bytecode of this method
|
||||||
code: Vec<u1>,
|
code: Vec<u1>,
|
||||||
/// The number of entries in the exception table
|
|
||||||
exception_table_length: u2,
|
|
||||||
/// The exception handlers for this method
|
/// The exception handlers for this method
|
||||||
exception_table: Vec<AttributeCodeException>,
|
exception_table: Vec<AttributeCodeException>,
|
||||||
attributes_count: u2,
|
|
||||||
/// The attributes of the code
|
/// The attributes of the code
|
||||||
attributes: Vec<AttributeInfo>,
|
attributes: Vec<AttributeInfo>,
|
||||||
},
|
},
|
||||||
|
|
@ -282,21 +162,19 @@ pub enum AttributeInfoInner {
|
||||||
},
|
},
|
||||||
/// Only on `MethodInfo`, indicates which checked exceptions might be thrown
|
/// Only on `MethodInfo`, indicates which checked exceptions might be thrown
|
||||||
Exceptions {
|
Exceptions {
|
||||||
number_of_exceptions: u2,
|
|
||||||
/// Must be a `Class` constant
|
/// Must be a `Class` constant
|
||||||
exception_index_table: Vec<u2>,
|
exception_index_table: Vec<u2>,
|
||||||
},
|
},
|
||||||
/// Only on a `ClassFile`. Specifies the inner classes of a class
|
/// Only on a `ClassFile`. Specifies the inner classes of a class
|
||||||
InnerClasses {
|
InnerClasses {
|
||||||
number_of_classes: u2,
|
|
||||||
classes: Vec<AttributeInnerClass>,
|
classes: Vec<AttributeInnerClass>,
|
||||||
},
|
},
|
||||||
/// Only on a `ClassFile`, required if it is local or anonymous
|
/// Only on a `ClassFile`, required if it is local or anonymous
|
||||||
EnclosingMethod {
|
EnclosingMethod {
|
||||||
/// Must be a `Class` constant, the innermost enclosing class
|
/// Must be a `Class` constant, the innermost enclosing class
|
||||||
class_index: FromPool,
|
class_index: FromPool<cp_info::Class>,
|
||||||
/// Must be zero or `NameAndType`
|
/// Must be zero or `NameAndType`
|
||||||
method_index: FromPool,
|
method_index: FromPool<cp_info::NameAndType>,
|
||||||
},
|
},
|
||||||
/// Can be on `ClassFile`, `FieldInfo`,or `MethodInfo`.
|
/// Can be on `ClassFile`, `FieldInfo`,or `MethodInfo`.
|
||||||
/// Every generated class has to have this attribute or the `Synthetic` Accessor modifier
|
/// Every generated class has to have this attribute or the `Synthetic` Accessor modifier
|
||||||
|
|
@ -304,12 +182,12 @@ pub enum AttributeInfoInner {
|
||||||
/// Can be on `ClassFile`, `FieldInfo`,or `MethodInfo`. Records generic signature information
|
/// Can be on `ClassFile`, `FieldInfo`,or `MethodInfo`. Records generic signature information
|
||||||
Signature {
|
Signature {
|
||||||
/// Must be `Utf8`, and a Class/Method/Field signature
|
/// Must be `Utf8`, and a Class/Method/Field signature
|
||||||
signature_index: FromPool,
|
signature_index: FromPool<cp_info::Utf8>,
|
||||||
},
|
},
|
||||||
/// Only on a `ClassFile`
|
/// Only on a `ClassFile`
|
||||||
SourceFile {
|
SourceFile {
|
||||||
/// Must be `Utf8`, the name of the source filed
|
/// Must be `Utf8`, the name of the source filed
|
||||||
sourcefile_index: FromPool,
|
sourcefile_index: FromPool<cp_info::Utf8>,
|
||||||
},
|
},
|
||||||
/// Only on a `ClassFile`
|
/// Only on a `ClassFile`
|
||||||
SourceDebugExtension {
|
SourceDebugExtension {
|
||||||
|
|
@ -318,18 +196,15 @@ pub enum AttributeInfoInner {
|
||||||
},
|
},
|
||||||
/// Only on the `Code` attribute. It includes line number information used by debuggers
|
/// Only on the `Code` attribute. It includes line number information used by debuggers
|
||||||
LineNumberTable {
|
LineNumberTable {
|
||||||
line_number_table_length: u2,
|
|
||||||
line_number_table: Vec<AttributeLineNumber>,
|
line_number_table: Vec<AttributeLineNumber>,
|
||||||
},
|
},
|
||||||
/// Only on the `Code` attribute. It may be used to determine the value of local variables by debuggers
|
/// Only on the `Code` attribute. It may be used to determine the value of local variables by debuggers
|
||||||
LocalVariableTable {
|
LocalVariableTable {
|
||||||
local_variable_table_length: u2,
|
|
||||||
/// Note: the 3rd field is called `descriptor_index` and represents an field descriptor
|
/// Note: the 3rd field is called `descriptor_index` and represents an field descriptor
|
||||||
local_variable_table: Vec<AttributeLocalVariableTable>,
|
local_variable_table: Vec<AttributeLocalVariableTable>,
|
||||||
},
|
},
|
||||||
/// Only on the `Code` attribute. It provides signature information instead of descriptor information
|
/// Only on the `Code` attribute. It provides signature information instead of descriptor information
|
||||||
LocalVariableTypeTable {
|
LocalVariableTypeTable {
|
||||||
local_variable_table_length: u2,
|
|
||||||
/// Note: the 3rd field is called `signature_index` and represents a field type signature
|
/// Note: the 3rd field is called `signature_index` and represents a field type signature
|
||||||
local_variable_table: Vec<AttributeLocalVariableTable>,
|
local_variable_table: Vec<AttributeLocalVariableTable>,
|
||||||
},
|
},
|
||||||
|
|
@ -337,22 +212,18 @@ pub enum AttributeInfoInner {
|
||||||
Deprecated,
|
Deprecated,
|
||||||
/// Can be on `ClassFile`, `FieldInfo`,or `MethodInfo`. Contains all Runtime visible annotations
|
/// Can be on `ClassFile`, `FieldInfo`,or `MethodInfo`. Contains all Runtime visible annotations
|
||||||
RuntimeVisibleAnnotations {
|
RuntimeVisibleAnnotations {
|
||||||
num_annotations: u2,
|
|
||||||
annotations: Vec<Annotation>,
|
annotations: Vec<Annotation>,
|
||||||
},
|
},
|
||||||
/// Same as `RuntimeVisibleAnnotations`, but invisible to reflection
|
/// Same as `RuntimeVisibleAnnotations`, but invisible to reflection
|
||||||
RuntimeInvisibleAnnotations {
|
RuntimeInvisibleAnnotations {
|
||||||
num_annotations: u2,
|
|
||||||
annotations: Vec<Annotation>,
|
annotations: Vec<Annotation>,
|
||||||
},
|
},
|
||||||
/// Only on `MethodInfo`, parameter annotations visible during runtime
|
/// Only on `MethodInfo`, parameter annotations visible during runtime
|
||||||
RuntimeVisibleParameterAnnotations {
|
RuntimeVisibleParameterAnnotations {
|
||||||
num_parameters: u1,
|
|
||||||
parameter_annotations: Vec<ParameterAnnotation>,
|
parameter_annotations: Vec<ParameterAnnotation>,
|
||||||
},
|
},
|
||||||
/// Same as `RuntimeVisibleParameterAnnotations`, but invisible to reflection
|
/// Same as `RuntimeVisibleParameterAnnotations`, but invisible to reflection
|
||||||
RuntimeInvisibleParameterAnnotations {
|
RuntimeInvisibleParameterAnnotations {
|
||||||
num_parameters: u1,
|
|
||||||
parameter_annotations: Vec<ParameterAnnotation>,
|
parameter_annotations: Vec<ParameterAnnotation>,
|
||||||
},
|
},
|
||||||
/// Only on `MethodInfo`, on those representing elements of annotation types, the default value of the element
|
/// Only on `MethodInfo`, on those representing elements of annotation types, the default value of the element
|
||||||
|
|
@ -361,7 +232,6 @@ pub enum AttributeInfoInner {
|
||||||
},
|
},
|
||||||
/// Only on `ClassFile`. Records bootstrap method specifiers for `invokedynamic`
|
/// Only on `ClassFile`. Records bootstrap method specifiers for `invokedynamic`
|
||||||
BootstrapMethods {
|
BootstrapMethods {
|
||||||
num_bootstrap_methods: u2,
|
|
||||||
bootstrap_methods: Vec<BootstrapMethod>,
|
bootstrap_methods: Vec<BootstrapMethod>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -422,9 +292,7 @@ pub enum StackMapFrame {
|
||||||
FullFrame {
|
FullFrame {
|
||||||
frame_type: u1, //255
|
frame_type: u1, //255
|
||||||
offset_delta: u2,
|
offset_delta: u2,
|
||||||
number_of_locals: u2,
|
|
||||||
locals: Vec<VerificationTypeInfo>,
|
locals: Vec<VerificationTypeInfo>,
|
||||||
number_of_stack_items: u2,
|
|
||||||
stack: Vec<VerificationTypeInfo>,
|
stack: Vec<VerificationTypeInfo>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -456,7 +324,7 @@ pub enum VerificationTypeInfo {
|
||||||
Object {
|
Object {
|
||||||
tag: u1, // 7
|
tag: u1, // 7
|
||||||
/// Must be a `Class`
|
/// Must be a `Class`
|
||||||
cpool_index: u2,
|
cpool_index: FromPool<cp_info::Class>,
|
||||||
},
|
},
|
||||||
Uninitialized {
|
Uninitialized {
|
||||||
tag: u1, // 8
|
tag: u1, // 8
|
||||||
|
|
@ -468,11 +336,11 @@ pub enum VerificationTypeInfo {
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub struct AttributeInnerClass {
|
pub struct AttributeInnerClass {
|
||||||
/// Must be a `Class`
|
/// Must be a `Class`
|
||||||
pub inner_class_info_index: FromPool,
|
pub inner_class_info_index: FromPool<cp_info::Class>,
|
||||||
/// Must be 0 or a `Class`
|
/// Must be 0 or a `Class`
|
||||||
pub outer_class_info_index: FromPool,
|
pub outer_class_info_index: FromPool<cp_info::Class>,
|
||||||
/// Must be 0 or `Utf8`
|
/// Must be 0 or `Utf8`
|
||||||
pub inner_class_name_index: FromPool,
|
pub inner_class_name_index: FromPool<cp_info::Utf8>,
|
||||||
/// Must be a mask of `InnerClassAccessFlags`
|
/// Must be a mask of `InnerClassAccessFlags`
|
||||||
pub inner_class_access_flags: u2,
|
pub inner_class_access_flags: u2,
|
||||||
}
|
}
|
||||||
|
|
@ -494,9 +362,9 @@ pub struct AttributeLocalVariableTable {
|
||||||
/// The local variable must have a value between `start_pc` and `start_pc + length`
|
/// The local variable must have a value between `start_pc` and `start_pc + length`
|
||||||
pub length: u2,
|
pub length: u2,
|
||||||
/// Must be `Utf8`
|
/// Must be `Utf8`
|
||||||
pub name_index: FromPool,
|
pub name_index: FromPool<cp_info::Utf8>,
|
||||||
/// Must be `Utf8`, field descriptor or field signature encoding the type
|
/// Must be `Utf8`, field descriptor or field signature encoding the type
|
||||||
pub descriptor_or_signature_index: FromPool,
|
pub descriptor_or_signature_index: FromPool<cp_info::Utf8>,
|
||||||
/// The variable must be at `index` in the local variable array
|
/// The variable must be at `index` in the local variable array
|
||||||
pub index: u2,
|
pub index: u2,
|
||||||
}
|
}
|
||||||
|
|
@ -505,7 +373,7 @@ pub struct AttributeLocalVariableTable {
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct Annotation {
|
pub struct Annotation {
|
||||||
/// Must be `Utf8`
|
/// Must be `Utf8`
|
||||||
pub type_index: FromPool,
|
pub type_index: FromPool<cp_info::Utf8>,
|
||||||
pub num_element_value_pairs: u2,
|
pub num_element_value_pairs: u2,
|
||||||
pub element_value_pairs: Vec<AnnotationElementValuePair>,
|
pub element_value_pairs: Vec<AnnotationElementValuePair>,
|
||||||
}
|
}
|
||||||
|
|
@ -516,7 +384,7 @@ pub struct Annotation {
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct AnnotationElementValuePair {
|
pub struct AnnotationElementValuePair {
|
||||||
/// Must be `Utf8`
|
/// Must be `Utf8`
|
||||||
pub element_name_index: FromPool,
|
pub element_name_index: FromPool<cp_info::Utf8>,
|
||||||
pub element_name_name: AnnotationElementValue,
|
pub element_name_name: AnnotationElementValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -534,19 +402,19 @@ pub enum AnnotationElementValueValue {
|
||||||
/// If the tag is B, C, D, F, I, J, S, Z, or s.
|
/// If the tag is B, C, D, F, I, J, S, Z, or s.
|
||||||
ConstValueIndex {
|
ConstValueIndex {
|
||||||
/// Must be the matching constant pool entry
|
/// Must be the matching constant pool entry
|
||||||
index: FromPool,
|
index: FromPool<CpInfoInner>,
|
||||||
},
|
},
|
||||||
/// If the tag is e
|
/// If the tag is e
|
||||||
EnumConstValue {
|
EnumConstValue {
|
||||||
/// Must be `Utf8`
|
/// Must be `Utf8`
|
||||||
type_name_index: FromPool,
|
type_name_index: FromPool<cp_info::Utf8>,
|
||||||
/// Must be `Utf8`
|
/// Must be `Utf8`
|
||||||
const_name_index: FromPool,
|
const_name_index: FromPool<cp_info::Utf8>,
|
||||||
},
|
},
|
||||||
/// If the tag is c
|
/// If the tag is c
|
||||||
ClassInfoIndex {
|
ClassInfoIndex {
|
||||||
/// Must be `Utf8`, for example Ljava/lang/Object; for Object
|
/// Must be `Utf8`, for example Ljava/lang/Object; for Object
|
||||||
index: FromPool,
|
index: FromPool<cp_info::Utf8>,
|
||||||
},
|
},
|
||||||
/// If the tag is @
|
/// If the tag is @
|
||||||
AnnotationValue {
|
AnnotationValue {
|
||||||
|
|
@ -554,16 +422,12 @@ pub enum AnnotationElementValueValue {
|
||||||
annotation: Box<Annotation>,
|
annotation: Box<Annotation>,
|
||||||
},
|
},
|
||||||
/// If the tag is [
|
/// If the tag is [
|
||||||
ArrayValue {
|
ArrayValue { values: Vec<AnnotationElementValue> },
|
||||||
num_values: u2,
|
|
||||||
values: Vec<AnnotationElementValue>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in `AttributeInfo::RuntimeVisibleParameterAnnotations`
|
/// Used in `AttributeInfo::RuntimeVisibleParameterAnnotations`
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct ParameterAnnotation {
|
pub struct ParameterAnnotation {
|
||||||
pub num_annotations: u2,
|
|
||||||
pub annotations: Vec<Annotation>,
|
pub annotations: Vec<Annotation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -571,10 +435,9 @@ pub struct ParameterAnnotation {
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct BootstrapMethod {
|
pub struct BootstrapMethod {
|
||||||
/// Must be a `MethodHandle`
|
/// Must be a `MethodHandle`
|
||||||
pub bootstrap_method_ref: FromPool,
|
pub bootstrap_method_ref: FromPool<cp_info::MethodHandle>,
|
||||||
pub num_bootstrap_arguments: u2,
|
|
||||||
/// Each argument is a cpool entry. The constants must be `String, Class, Integer, Long, Float, Double, MethodHandle, or MethodType`
|
/// Each argument is a cpool entry. The constants must be `String, Class, Integer, Long, Float, Double, MethodHandle, or MethodType`
|
||||||
pub bootstrap_arguments: Vec<FromPool>,
|
pub bootstrap_arguments: Vec<FromPool<CpInfoInner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/////// Access Flags
|
/////// Access Flags
|
||||||
|
|
@ -42,7 +42,7 @@ fn parse_empty_class() {
|
||||||
|
|
||||||
assert_eq!(parsed.minor_version, 0);
|
assert_eq!(parsed.minor_version, 0);
|
||||||
assert_eq!(parsed.major_version, 0x003b);
|
assert_eq!(parsed.major_version, 0x003b);
|
||||||
assert_eq!(parsed.constant_pool_count, 0x000d);
|
assert_eq!(parsed.constant_pool.len() + 1, 0x000d);
|
||||||
assert_eq!(parsed.constant_pool.len(), 12);
|
assert_eq!(parsed.constant_pool.len(), 12);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parsed.constant_pool,
|
parsed.constant_pool,
|
||||||
|
|
@ -134,17 +134,15 @@ fn parse_empty_class() {
|
||||||
assert_eq!(parsed.access_flags, 0x0021);
|
assert_eq!(parsed.access_flags, 0x0021);
|
||||||
assert_eq!(parsed.this_class, 7.into());
|
assert_eq!(parsed.this_class, 7.into());
|
||||||
assert_eq!(parsed.super_class, 2.into());
|
assert_eq!(parsed.super_class, 2.into());
|
||||||
assert_eq!(parsed.interfaces_count, 0);
|
assert_eq!(parsed.interfaces.len(), 0);
|
||||||
assert_eq!(parsed.interfaces, vec![]);
|
assert_eq!(parsed.interfaces, vec![]);
|
||||||
assert_eq!(parsed.fields_count, 0);
|
assert_eq!(parsed.fields.len(), 0);
|
||||||
assert_eq!(parsed.fields, vec![]);
|
assert_eq!(parsed.fields, vec![]);
|
||||||
assert_eq!(parsed.method_count, 1);
|
assert_eq!(parsed.methods.len(), 1);
|
||||||
assert_eq!(parsed.methods[0].access_flags, 1);
|
assert_eq!(parsed.methods[0].access_flags, 1);
|
||||||
assert_eq!(parsed.methods[0].name_index, 5.into());
|
assert_eq!(parsed.methods[0].name_index, 5.into());
|
||||||
assert_eq!(parsed.methods[0].descriptor_index, 6.into());
|
assert_eq!(parsed.methods[0].descriptor_index, 6.into());
|
||||||
assert_eq!(parsed.methods[0].attributes_count, 1);
|
assert_eq!(parsed.methods[0].attributes.len(), 1);
|
||||||
//assert_eq!(parsed.methods[0].attributes[0].attribute_name_index, 9);
|
|
||||||
//assert_eq!(parsed.methods[0].attributes[0].attribute_length, 0x1d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
72
src/ui.rs
Normal file
72
src/ui.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
use crate::parse::{ClassFile, ParseErr};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
pub fn display_class<W: Write>(mut w: W, class: &ClassFile) -> Result<(), Box<dyn Error>> {
|
||||||
|
let cp = &class.constant_pool;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
".class ({:#X?}) file version {}.{}",
|
||||||
|
class.magic, class.major_version, class.minor_version
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(w)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"class {} extends {}{} {{",
|
||||||
|
&class.this_class.get(cp)?.name_index.get(cp)?,
|
||||||
|
if class.interfaces.len() == 0 {
|
||||||
|
"".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>>()?
|
||||||
|
.join(",")
|
||||||
|
)
|
||||||
|
},
|
||||||
|
match class.super_class.get(cp)? {
|
||||||
|
None => "<none>",
|
||||||
|
Some(class) => &class.name_index.get(cp)?,
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(w, " Class Attributes:")?;
|
||||||
|
for attr in &class.attributes {
|
||||||
|
writeln!(w, " {}", &attr.attribute_name_index.get(cp)?)?;
|
||||||
|
}
|
||||||
|
writeln!(w)?;
|
||||||
|
|
||||||
|
writeln!(w, " Class Fields:")?;
|
||||||
|
for field in &class.fields {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
" {} {}",
|
||||||
|
&field.descriptor_index.get(cp)?,
|
||||||
|
&field.name_index.get(cp)?
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
writeln!(w)?;
|
||||||
|
|
||||||
|
writeln!(w, " Class Methods:")?;
|
||||||
|
for method in &class.methods {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
" {} {}",
|
||||||
|
&method.descriptor_index.get(cp)?,
|
||||||
|
&method.name_index.get(cp)?,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
BIN
testdata/Test2.class
vendored
BIN
testdata/Test2.class
vendored
Binary file not shown.
6
testdata/Test2.java
vendored
6
testdata/Test2.java
vendored
|
|
@ -1,11 +1,13 @@
|
||||||
class Test2 {
|
class Test2 {
|
||||||
|
int myField;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
i++;
|
i++;
|
||||||
print(i);
|
new Test2().print(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print(int i) {
|
void print(int i) {
|
||||||
System.out.println(i);
|
System.out.println(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue