Class loading, object creation
This commit is contained in:
parent
25d3509ccf
commit
4dabd6c3a8
5 changed files with 235 additions and 39 deletions
|
@ -162,7 +162,18 @@ impl JavaClassFile {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_classname(&self) -> Result<String, Error> {
|
pub fn has_super_class(&self) -> bool {
|
||||||
|
return self.super_class != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_super_class_name(&self) -> Result<&String, Error> {
|
||||||
|
let super_class_entry = self.pool_class_entry(self.super_class)?;
|
||||||
|
let super_name_entry = self.pool_utf8_entry(super_class_entry.name_index)?;
|
||||||
|
|
||||||
|
return Ok(&super_name_entry.utf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_classname(&self) -> Result<&String, Error> {
|
||||||
let class_info_entry = self.pool_entry(self.this_class)?;
|
let class_info_entry = self.pool_entry(self.this_class)?;
|
||||||
|
|
||||||
let class_info_entry = match class_info_entry {
|
let class_info_entry = match class_info_entry {
|
||||||
|
@ -176,7 +187,7 @@ impl JavaClassFile {
|
||||||
_ => return Err(Error::BadFileError(format!("Invalid class_info.name_index from this_class, expected index to Utf8 but found {:?}", name_entry)))
|
_ => return Err(Error::BadFileError(format!("Invalid class_info.name_index from this_class, expected index to Utf8 but found {:?}", name_entry)))
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(name_entry.utf8.clone());
|
return Ok(&name_entry.utf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,10 @@ impl ClassStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn class_count(&self) -> usize {
|
||||||
|
return self.classes.len();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_class(&mut self, class_file: JavaClassFile, was_init: bool) {
|
pub fn add_class(&mut self, class_file: JavaClassFile, was_init: bool) {
|
||||||
return self.classes.push((was_init, class_file));
|
return self.classes.push((was_init, class_file));
|
||||||
}
|
}
|
||||||
|
@ -70,7 +74,7 @@ impl ClassStore {
|
||||||
let classfile = JavaClassFile::new(&mut file_reader)?;
|
let classfile = JavaClassFile::new(&mut file_reader)?;
|
||||||
|
|
||||||
let classname = classfile.get_classname()?;
|
let classname = classfile.get_classname()?;
|
||||||
self.class_ids.insert(classname, self.classes.len());
|
self.class_ids.insert(classname.to_string(), self.classes.len());
|
||||||
self.classes.push((false, classfile));
|
self.classes.push((false, classfile));
|
||||||
|
|
||||||
return Ok(self.classes.len() - 1);
|
return Ok(self.classes.len() - 1);
|
||||||
|
@ -121,14 +125,14 @@ impl ClassStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn class_idx_from_name(&self, classname: &String) -> Option<&usize> {
|
pub fn class_idx_from_name(&self, classname: &String) -> Option<usize> {
|
||||||
return self.class_ids.get(classname);
|
return self.class_ids.get(classname).copied();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn was_init(&self, classname: &String) -> Option<&bool> {
|
pub fn was_init(&self, classname: &String) -> Option<bool> {
|
||||||
let (was_init, _) = self.classes.get(*self.class_idx_from_name(classname).unwrap()).unwrap();
|
let (was_init, _) = self.classes.get(self.class_idx_from_name(classname).unwrap()).unwrap();
|
||||||
|
|
||||||
return Some(was_init);
|
return Some(*was_init);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_init(&mut self, class_idx: usize, was_init: bool) {
|
pub fn set_init(&mut self, class_idx: usize, was_init: bool) {
|
||||||
|
|
134
src/heap_area.rs
134
src/heap_area.rs
|
@ -1,7 +1,10 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use core::fmt::Debug as DebugTrait;
|
||||||
|
|
||||||
use crate::accessmasks::FieldAccessFlag;
|
use crate::accessmasks::FieldAccessFlag;
|
||||||
use crate::classfile::{ JavaClassFile, AbstractTypeDescription, MethodInfo, AbstractTypeKind };
|
use crate::classfile::{ JavaClassFile, AbstractTypeDescription, MethodInfo, AbstractTypeKind };
|
||||||
|
use crate::classstore::ClassStore;
|
||||||
use crate::jvm::Error;
|
use crate::jvm::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -22,6 +25,13 @@ impl HeapArea {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_object(&mut self, class_store: &ClassStore, class_index: usize) -> ObjectReference {
|
||||||
|
let (object_ref, object_size) = self.object_area.make(class_store, class_index);
|
||||||
|
self.memory_used += object_size;
|
||||||
|
|
||||||
|
return object_ref;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_static(&mut self, class: &JavaClassFile, class_index: usize) {
|
pub fn make_static(&mut self, class: &JavaClassFile, class_index: usize) {
|
||||||
self.memory_used += self.static_area.make(class, class_index);
|
self.memory_used += self.static_area.make(class, class_index);
|
||||||
}
|
}
|
||||||
|
@ -30,27 +40,144 @@ impl HeapArea {
|
||||||
pub type ObjectReference=u32;
|
pub type ObjectReference=u32;
|
||||||
|
|
||||||
const DEFAULT_COMPARTMENT_CAPACITY: usize = u16::MAX as usize;
|
const DEFAULT_COMPARTMENT_CAPACITY: usize = u16::MAX as usize;
|
||||||
|
const INVALID_FIRST_OBJECT: usize = u16::MAX as usize;
|
||||||
|
const INVALID_NEXT_COMPARTMENT: usize = 0;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct ObjectArea {
|
pub struct ObjectArea {
|
||||||
compartments: Vec<ObjectCompartment>,
|
compartments: Vec<ObjectCompartment>,
|
||||||
|
first_free_compartment: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectArea {
|
||||||
|
pub fn make(&mut self, class_store: &ClassStore, target_class_index: usize) -> (ObjectReference, usize) {
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
let mut current_class_index;
|
||||||
|
let mut next_class_index = target_class_index;
|
||||||
|
|
||||||
|
let mut object_size = 0;
|
||||||
|
|
||||||
|
while next_class_index != class_store.class_count() {
|
||||||
|
current_class_index = next_class_index;
|
||||||
|
|
||||||
|
let class = class_store.class_file_from_idx(current_class_index).unwrap();
|
||||||
|
|
||||||
|
for field in &class.fields {
|
||||||
|
let new_field = ObjectField {
|
||||||
|
value: FieldValue::default_for(&field.descriptor)
|
||||||
|
};
|
||||||
|
object_size += std::mem::size_of_val(&new_field);
|
||||||
|
|
||||||
|
fields.push(new_field);
|
||||||
|
}
|
||||||
|
|
||||||
|
if class.has_super_class() {
|
||||||
|
next_class_index = class_store.class_idx_from_name(class.get_super_class_name().unwrap()).unwrap();
|
||||||
|
} else {
|
||||||
|
next_class_index = class_store.class_count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_object = HeapObject {
|
||||||
|
class_index: target_class_index,
|
||||||
|
fields: fields.into_boxed_slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
object_size += std::mem::size_of_val(&new_object);
|
||||||
|
object_size += std::mem::size_of_val(&new_object.fields);
|
||||||
|
|
||||||
|
let object_ref = self.store_object(new_object);
|
||||||
|
|
||||||
|
return (object_ref, object_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_object(&mut self, object: HeapObject) -> ObjectReference {
|
||||||
|
if self.first_free_compartment == 0 {
|
||||||
|
self.compartments.push(ObjectCompartment::new(INVALID_NEXT_COMPARTMENT));
|
||||||
|
self.first_free_compartment = self.compartments.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
let compartment_index = self.first_free_compartment - 1;
|
||||||
|
let object_index = self.compartments.get_mut(compartment_index).unwrap().store(object);
|
||||||
|
if self.compartments[compartment_index].first_free == INVALID_FIRST_OBJECT {
|
||||||
|
self.first_free_compartment = self.compartments[compartment_index].next_free_compartment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return object_index + (DEFAULT_COMPARTMENT_CAPACITY as u32 * (compartment_index+1) as u32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ObjectCompartment {
|
pub struct ObjectCompartment {
|
||||||
objects: Box<[ObjectCompartmentEntry]>,
|
objects: Box<[ObjectCompartmentEntry]>,
|
||||||
first_free: usize,
|
first_free: usize,
|
||||||
reserved_count: usize,
|
reserved_count: usize,
|
||||||
|
next_free_compartment: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectCompartment {
|
||||||
|
fn new(next_free: usize) -> Self {
|
||||||
|
let mut os = Vec::with_capacity(DEFAULT_COMPARTMENT_CAPACITY);
|
||||||
|
for i in 0..DEFAULT_COMPARTMENT_CAPACITY-1 {
|
||||||
|
os.push(ObjectCompartmentEntry::EmptyNext(i+1));
|
||||||
|
}
|
||||||
|
os.push(ObjectCompartmentEntry::EmptyTail());
|
||||||
|
|
||||||
|
ObjectCompartment {
|
||||||
|
objects: os.into_boxed_slice(),
|
||||||
|
first_free: 0,
|
||||||
|
reserved_count: 0,
|
||||||
|
next_free_compartment: next_free,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store(&mut self, object: HeapObject) -> ObjectReference {
|
||||||
|
let store_slot = self.objects.get(self.first_free).unwrap();
|
||||||
|
let compartment_index = self.first_free;
|
||||||
|
|
||||||
|
match store_slot {
|
||||||
|
ObjectCompartmentEntry::Valid(_) => unreachable!(),
|
||||||
|
ObjectCompartmentEntry::EmptyNext(next) => {
|
||||||
|
self.first_free = *next;
|
||||||
|
},
|
||||||
|
ObjectCompartmentEntry::EmptyTail() => {
|
||||||
|
// TODO: Maybe change to something else
|
||||||
|
self.first_free = INVALID_FIRST_OBJECT;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.objects.get_mut(compartment_index).unwrap() = ObjectCompartmentEntry::Valid(object);
|
||||||
|
self.reserved_count += 1;
|
||||||
|
|
||||||
|
return compartment_index as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebugTrait for ObjectCompartment {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
f.debug_struct("ObjectCompartment")
|
||||||
|
.field("first_free", &self.first_free)
|
||||||
|
.field("reserved_count", &self.reserved_count)
|
||||||
|
.field("next_free_compartment", &self.next_free_compartment)
|
||||||
|
.field("objects", &self.objects
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|e| match e { (_, ObjectCompartmentEntry::Valid(_)) => true, _ => false})
|
||||||
|
.map(|e| match e { (i, ObjectCompartmentEntry::Valid(o)) => (i, o), _ => unreachable!()})
|
||||||
|
.collect::<Vec<(usize, &HeapObject)>>()
|
||||||
|
).finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ObjectCompartmentEntry {
|
pub enum ObjectCompartmentEntry {
|
||||||
Valid(HeapObject),
|
Valid(HeapObject),
|
||||||
Empty(usize), // next empty value
|
EmptyNext(usize),
|
||||||
|
EmptyTail(), // next empty value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct HeapObject {
|
pub struct HeapObject {
|
||||||
|
class_index: usize,
|
||||||
fields: Box<[ObjectField]>,
|
fields: Box<[ObjectField]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +232,7 @@ impl StaticArea {
|
||||||
let field_array_size = std::mem::size_of_val(&new_object.fields);
|
let field_array_size = std::mem::size_of_val(&new_object.fields);
|
||||||
let method_array_size = std::mem::size_of_val(&new_object.methods);
|
let method_array_size = std::mem::size_of_val(&new_object.methods);
|
||||||
|
|
||||||
let _ = self.static_objects.insert(class.get_classname().unwrap(), new_object);
|
let _ = self.static_objects.insert(class.get_classname().unwrap().to_string(), new_object);
|
||||||
|
|
||||||
return object_memory_size + field_array_size + fields_cumulative_size + method_array_size;
|
return object_memory_size + field_array_size + fields_cumulative_size + method_array_size;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +265,6 @@ pub struct StaticField {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ObjectField {
|
pub struct ObjectField {
|
||||||
pub type_description: AbstractTypeDescription,
|
|
||||||
pub value: FieldValue,
|
pub value: FieldValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
107
src/jvm.rs
107
src/jvm.rs
|
@ -10,7 +10,7 @@ use crate::classfile::{ JavaClassFile, MethodInfo, MethodDescriptor, AbstractTyp
|
||||||
use crate::classstore;
|
use crate::classstore;
|
||||||
use crate::classstore::ClassStore;
|
use crate::classstore::ClassStore;
|
||||||
use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo};
|
use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo};
|
||||||
use crate::heap_area::{ HeapArea, FieldValue };
|
use crate::heap_area::{ HeapArea, FieldValue, ObjectReference };
|
||||||
use crate::stackframe;
|
use crate::stackframe;
|
||||||
use crate::stackframe::{ StackFrame, StackValue, OperandStack };
|
use crate::stackframe::{ StackFrame, StackValue, OperandStack };
|
||||||
|
|
||||||
|
@ -128,6 +128,10 @@ impl JVM {
|
||||||
);
|
);
|
||||||
|
|
||||||
self.class_store.add_class(entry_class, true);
|
self.class_store.add_class(entry_class, true);
|
||||||
|
self.class_store.load_class(&"java/lang/Class".to_string())?;
|
||||||
|
self.class_store.load_class(&"java/lang/Object".to_string())?;
|
||||||
|
|
||||||
|
self.new_object(&"java/lang/Class".to_string())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -156,12 +160,13 @@ impl JVM {
|
||||||
JVMCallbackOperation::PushFrame(frame) => self.stack_frames.push(frame),
|
JVMCallbackOperation::PushFrame(frame) => self.stack_frames.push(frame),
|
||||||
|
|
||||||
JVMCallbackOperation::LoadClass(name) => {
|
JVMCallbackOperation::LoadClass(name) => {
|
||||||
self.class_store.load_class(&name)?;
|
// TODO: throw exception
|
||||||
()
|
self.load_class_hierarchy(&name)?;
|
||||||
},
|
},
|
||||||
|
|
||||||
JVMCallbackOperation::InitClass(name) => {
|
JVMCallbackOperation::InitClass(name) => {
|
||||||
self.init_class(*self.class_store.class_idx_from_name(&name).unwrap())?;
|
// TODO: throw exception
|
||||||
|
self.init_class_hierarchy(&name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,13 +174,82 @@ impl JVM {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_class(&mut self, class_idx: usize) -> Result<(), Error> {
|
fn new_object(&mut self, class_name: &String) -> Result<ObjectReference, Error> {
|
||||||
|
let (_, class_idx) = self.class_store.get_class(class_name)?;
|
||||||
|
Ok(self.heap_area.make_object(&mut self.class_store, class_idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_class_hierarchy(&mut self, name: &String) -> Result<(), Error> {
|
||||||
|
self.class_store.load_class(&name)?;
|
||||||
|
let super_class_name = {
|
||||||
|
let (file, _) = self.class_store.get_class(&name)?;
|
||||||
|
file.get_super_class_name()?.clone()
|
||||||
|
};
|
||||||
|
let mut super_classes = vec![super_class_name];
|
||||||
|
|
||||||
|
while super_classes.len() != 0 {
|
||||||
|
let current_super = super_classes.pop().unwrap();
|
||||||
|
let have_super_super = {
|
||||||
|
let (super_file, _) = self.class_store.get_class(¤t_super)?;
|
||||||
|
super_file.has_super_class()
|
||||||
|
};
|
||||||
|
|
||||||
|
if have_super_super {
|
||||||
|
let super_super_name = {
|
||||||
|
let (super_file, _) = self.class_store.get_class(¤t_super)?;
|
||||||
|
super_file.get_super_class_name()?
|
||||||
|
};
|
||||||
|
if self.class_store.have_class(super_super_name) {
|
||||||
|
self.class_store.load_class(¤t_super)?;
|
||||||
|
} else {
|
||||||
|
super_classes.push(current_super);
|
||||||
|
super_classes.push(super_super_name.to_string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.class_store.load_class(¤t_super)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_class_hierarchy(&mut self, name: &String) -> Result<(), Error> {
|
||||||
|
let mut class_stack = vec![name.to_string()];
|
||||||
|
|
||||||
|
while class_stack.len() != 0 {
|
||||||
|
let current_name = class_stack.pop().unwrap();
|
||||||
|
let was_super_init = {
|
||||||
|
let (file, _) = self.class_store.get_class(¤t_name)?;
|
||||||
|
if ! file.has_super_class() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let super_name = file.get_super_class_name()?;
|
||||||
|
self.class_store.was_init(super_name).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if was_super_init {
|
||||||
|
let class_idx = self.class_store.class_idx_from_name(¤t_name).unwrap();
|
||||||
|
self.init_class(class_idx)?;
|
||||||
|
} else {
|
||||||
|
let super_name = {
|
||||||
|
let (file, _) = self.class_store.get_class(¤t_name)?;
|
||||||
|
file.get_super_class_name()?
|
||||||
|
};
|
||||||
|
class_stack.push(current_name);
|
||||||
|
class_stack.push(super_name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_class(&mut self, class_idx: usize) -> Result<(), Error> {
|
||||||
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
||||||
let clinit_idx = class_file.find_method_index(&"<clinit>".to_string());
|
let clinit_idx = class_file.find_method_index(&"<clinit>".to_string());
|
||||||
|
|
||||||
self.heap_area.make_static(class_file, class_idx);
|
self.heap_area.make_static(class_file, class_idx);
|
||||||
|
|
||||||
// TODO: ConstantValue Attributes (final)
|
|
||||||
for field in &class_file.fields {
|
for field in &class_file.fields {
|
||||||
if field.access_flags & FieldAccessFlag::Static {
|
if field.access_flags & FieldAccessFlag::Static {
|
||||||
let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter()
|
let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter()
|
||||||
|
@ -211,7 +285,7 @@ impl JVM {
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.heap_area.static_area.set(&class_file.get_classname()?, &field.name, field_value)?;
|
self.heap_area.static_area.set(class_file.get_classname()?, &field.name, field_value)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,25 +297,6 @@ impl JVM {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_invoke_static(&mut self, class_index: usize, method_name: &String, arguments: &[StackValue]) -> Result<(), Error> {
|
|
||||||
|
|
||||||
let class_file = self.class_store.class_file_from_idx(class_index).unwrap();
|
|
||||||
|
|
||||||
let method_index = class_file.find_method_index(method_name)
|
|
||||||
.ok_or(Error::BadNameError(format!("Could not find method '{}' in class '{}'", method_name, class_file.get_classname()?)))?;
|
|
||||||
|
|
||||||
let new_frame = StackFrame::new(
|
|
||||||
class_file,
|
|
||||||
class_index,
|
|
||||||
method_index.try_into().expect(&format!("Bad method index: {}", method_index)),
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
|
|
||||||
self.stack_frames.push(new_frame);
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bytecode_loop(&mut self) -> Result<JVMCallbackOperation, Error> {
|
fn bytecode_loop(&mut self) -> Result<JVMCallbackOperation, Error> {
|
||||||
|
|
||||||
let frame = {
|
let frame = {
|
||||||
|
|
|
@ -26,6 +26,6 @@ fn main() {
|
||||||
Err(e) => println!("{:#?}", e),
|
Err(e) => println!("{:#?}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("{:#?}", jvm);
|
//println!("{:#?}", jvm.heap_area);
|
||||||
//println!("{:#?}", JavaClassFile::new(&mut File::open("java/Class.class").unwrap()));
|
//println!("{:#?}", JavaClassFile::new(&mut File::open("java/Class.class").unwrap()));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue