use std::collections::HashMap; use std::fmt; use std::fmt::{Debug, Formatter}; use std::rc::Rc; use crate::classloader::io::read_u16; use crate::vm::opcodes::Opcode; /// This is the class representation when the bytecode had just been loaded. pub(crate) struct ClassDef { pub minor_version: u16, pub major_version: u16, pub constant_pool: Rc>, pub access_flags: u16, this_class: u16, pub super_class: Option, pub interfaces: Vec, pub fields: HashMap, pub methods: HashMap, pub attributes: HashMap, } impl Debug for ClassDef{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.cp_class_name(&self.this_class)) } } impl ClassDef { pub fn new( minor_version: u16, major_version: u16, constant_pool: Rc>, access_flags: u16, this_class: u16, super_class: Option, interfaces: Vec, fields: HashMap, methods: HashMap, attributes: HashMap, ) -> Self { Self { major_version, minor_version, constant_pool, access_flags, this_class, super_class, interfaces, fields, methods, attributes, } } /// the bytecode version pub fn version(&self) -> (u16, u16) { (self.major_version, self.minor_version) } /// get the class name pub fn name(&self) -> &str { self.cp_class_name(&self.this_class) } pub fn get_method(&self, name: &str) -> Option<&Method> { self.methods.get(name) } pub fn has_method(&self, name: &str) -> bool { self.methods.contains_key(name) } pub fn cp_field_ref(&self, index: &u16) -> (&u16, &u16) { if let CpEntry::Fieldref(class_index, name_and_type_index) = self.constant_pool.get(index).unwrap() { (class_index, name_and_type_index) } else { unreachable!("should be field") } } /// both methodRef and InterfaceMethodRef /// returns (class_index, name_and_type_index) pub fn cp_method_ref(&self, index: &u16) -> (&u16, &u16) { if let CpEntry::MethodRef(class_index, name_and_type_index) | CpEntry::InterfaceMethodref(class_index, name_and_type_index) = self.constant_pool.get(index).unwrap() { (class_index, name_and_type_index) } else { unreachable!("should be method") } } pub fn cp_class_name(&self, index: &u16) -> &String { let cr = self.cp_class_ref(index); self.cp_utf8(cr) } pub fn cp_class_ref(&self, index: &u16) -> &u16 { if let CpEntry::ClassRef(name_index) = self.constant_pool.get(index).unwrap() { name_index } else { unreachable!("should be class entry") } } pub fn cp_utf8(&self, index: &u16) -> &String { if let CpEntry::Utf8(utf8) = self.constant_pool.get(index).unwrap() { utf8 } else { unreachable!("should be utf8 entry") } } pub fn cp_name_and_type(&self, index: &u16) -> (&u16, &u16) { if let CpEntry::NameAndType(name_index, type_index) = self.constant_pool.get(index).unwrap() { (name_index, type_index) } else { unreachable!("should be name_and_type entry") } } } #[derive(Debug)] pub enum CpEntry { Utf8(String), Integer(i32), Float(f32), Long(i64), Double(f64), ClassRef(u16), // (utf8) StringRef(u16), // (utf8) Fieldref(u16, u16), // (class, name_and_type) MethodRef(u16, u16), // (class, name_and_type) InterfaceMethodref(u16, u16), // (class, name_and_type) NameAndType(u16, u16), // (name, descriptor) MethodHandle(u8, u16), MethodType(u16), InvokeDynamic(u16, u16), } pub enum Modifier { Public = 0x0001, Private = 0x0002, Protected = 0x0004, Static = 0x0008, Final = 0x0010, Synchronized = 0x0020, Volatile = 0x0040, Transient = 0x0080, Native = 0x0100, Abstract = 0x0400, Strict = 0x0800, Synthetic = 0x1000, } //TODO implement more types #[derive(Debug)] pub enum AttributeType { ConstantValue(u16), Code(Box), StackMapTable, BootstrapMethods, NestHost, NestMembers, PermittedSubclasses, Exceptions, InnerClasses, EnclosingMethod, Synthetic, Signature, Record, SourceFile, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, SourceDebugExtension, Deprecated, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations, AnnotationDefault, MethodParameters, Module, ModulePackages, ModuleMainClass, } #[derive(Debug)] pub struct Exception { pub start_pc: u16, pub end_pc: u16, pub handler_pc: u16, pub catch_type: u16, } impl Exception { pub fn read(code: &[u8], index: &mut usize) -> Self { Self { start_pc: read_u16(code, index), end_pc: read_u16(code, index), handler_pc: read_u16(code, index), catch_type: read_u16(code, index), } } } #[derive(Debug)] pub struct MethodCode { _max_stack: u16, _max_locals: u16, pub(crate) opcodes: Vec, _exception_table: Vec, _code_attributes: HashMap, } impl MethodCode { pub(crate) fn new( _max_stack: u16, _max_locals: u16, code: Vec, _exception_table: Vec, _code_attributes: HashMap, ) -> Self { Self { _max_stack, _max_locals, opcodes: code, _exception_table, _code_attributes, } } } pub(crate) const _MODIFIERS: [(Modifier, &str); 12] = [ (Modifier::Public, "public "), (Modifier::Private, "private "), (Modifier::Protected, "protected "), (Modifier::Static, "static "), (Modifier::Final, "final "), (Modifier::Synchronized, "synchronized "), (Modifier::Volatile, "volatile "), (Modifier::Transient, "transient "), (Modifier::Native, "native "), (Modifier::Abstract, "abstract"), (Modifier::Strict, "strict"), (Modifier::Synthetic, "synthetic"), ]; pub struct Field { constant_pool: Rc>, access_flags: u16, pub(crate) name_index: u16, descriptor_index: u16, attributes: HashMap, _index: u16, } impl fmt::Debug for Field { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Field {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", self.access_flags, self.name_index, self.descriptor_index, self.attributes ) } } impl Field { pub fn new( constant_pool: Rc>, access_flags: u16, name_index: u16, descriptor_index: u16, attributes: HashMap, field_index: u16, ) -> Self { Field { constant_pool, access_flags, name_index, descriptor_index, attributes, _index: field_index, } } pub fn is(&self, modifier: Modifier) -> bool { let modifier = modifier as u16; self.access_flags & modifier == modifier } pub fn name(&self) -> &String { if let CpEntry::Utf8(utf8) = &self.constant_pool.get(&self.name_index).unwrap() { return utf8; } unreachable!() } pub fn type_of(&self) -> &String { if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { return s; } panic!() } } pub(crate) struct Method { pub(crate) constant_pool: Rc>, access_flags: u16, name_index: u16, descriptor_index: u16, pub(crate) attributes: HashMap, pub code: Vec, } impl Debug for Method { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, "Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", self.access_flags, self.name_index, self.descriptor_index, self.attributes ) } } impl Method { pub fn new( constant_pool: Rc>, access_flags: u16, name_index: u16, descriptor_index: u16, attributes: HashMap, code: Vec, ) -> Self { Method { constant_pool, access_flags, name_index, descriptor_index, attributes, code, } } pub fn name(&self) -> String { let mut full_name = String::new(); if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.name_index).unwrap() { full_name.push_str(s); } if let CpEntry::Utf8(s) = &self.constant_pool.get(&self.descriptor_index).unwrap() { full_name.push_str(s); } full_name } pub fn is(&self, modifier: Modifier) -> bool { let m = modifier as u16; (self.access_flags & m) == m } }