diff --git a/src/classfile.rs b/src/classfile.rs index 46b6c59..0522622 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -1,12 +1,11 @@ use std::io::Read; use std::error::Error as ErrorTrait; use core::fmt::{Display, Formatter, Debug}; -use core::mem::{ discriminant, Discriminant }; use core::str::Utf8Error; use crate::accessmasks::*; use crate::bytecode::Bytecode; -use crate::constantpool::{ ConstantPoolInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantClassInfo, ConstantNameAndTypeInfo }; +use crate::constantpool::{ ConstantPoolInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo }; #[derive(Debug)] pub enum Error { @@ -164,14 +163,14 @@ impl JavaClassFile { } pub fn get_classname(&self) -> Result { - let class_info_entry = pool_entry(&self.constant_pool, self.this_class as usize)?; + let class_info_entry = self.pool_entry(self.this_class)?; let class_info_entry = match class_info_entry { ConstantPoolInfo::Class(data) => data, _ => return Err(Error::BadFileError(format!("Invalid this_class index, expected index to ClassInfo but found {:?}", class_info_entry))) }; - let name_entry = pool_entry(&self.constant_pool, class_info_entry.name_index.into())?; + let name_entry = self.pool_entry(class_info_entry.name_index.into())?; let name_entry = match name_entry { ConstantPoolInfo::Utf8(utf8data) => utf8data, _ => return Err(Error::BadFileError(format!("Invalid class_info.name_index from this_class, expected index to Utf8 but found {:?}", name_entry))) @@ -192,28 +191,12 @@ impl JavaClassFile { return None; } - pub fn typed_pool_entry(&self, index: u16, variant: Discriminant) -> Result<&ConstantPoolInfo, Error> { - let pool_entry = &self.constant_pool[(index - 1) as usize]; - - if discriminant(pool_entry) != variant { - return Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type {:#?} but found {:#?}", index, self.get_classname()?, variant, discriminant(pool_entry)))); - } - - return Ok(pool_entry); + fn pool_entry(&self, index: u16) -> Result<&ConstantPoolInfo, Error> { + return pool_entry(&self.constant_pool, index); } pub fn pool_methodref_entry(&self, index: u16) -> Result<&ConstantMethodRefInfo, Error> { - let pool_entry = self.typed_pool_entry( - index, - discriminant( - &ConstantPoolInfo::MethodRef( - ConstantMethodRefInfo { - class_index: 0, - name_and_type_index: 0 - } - ) - ) - )?; + let pool_entry = self.pool_entry(index)?; let methodref_entry = match pool_entry { ConstantPoolInfo::MethodRef(data) => data, @@ -224,29 +207,38 @@ impl JavaClassFile { } pub fn pool_class_entry(&self, index: u16) -> Result<&ConstantClassInfo, Error> { - let pool_entry = self.typed_pool_entry(index, discriminant(&ConstantPoolInfo::Class(ConstantClassInfo {name_index: 0})))?; + let pool_entry = self.pool_entry(index)?; return match pool_entry { ConstantPoolInfo::Class(data) => Ok(data), - _ => unreachable!(), + _ => Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type Class but found {:?}", index, self.get_classname()?, pool_entry))) }; } pub fn pool_utf8_entry(&self, index: u16) -> Result<&ConstantUtf8Info, Error> { - let pool_entry = self.typed_pool_entry(index, discriminant(&ConstantPoolInfo::Utf8(ConstantUtf8Info {utf8: "".to_string()})))?; + let pool_entry = self.pool_entry(index)?; return match pool_entry { ConstantPoolInfo::Utf8(data) => Ok(data), - _ => unreachable!(), + _ => Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type Utf8 but found {:?}", index, self.get_classname()?, pool_entry))) + }; + } + + pub fn pool_int_entry(&self, index: u16) -> Result<&ConstantIntegerInfo, Error> { + let pool_entry = self.pool_entry(index)?; + + return match pool_entry { + ConstantPoolInfo::Integer(data) => Ok(data), + _ => Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type Integer but found {:?}", index, self.get_classname()?, pool_entry))) }; } pub fn pool_nameandtype_entry(&self, index: u16) -> Result<&ConstantNameAndTypeInfo, Error> { - let pool_entry = self.typed_pool_entry(index, discriminant(&ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo {name_index: 0, descriptor_index: 0})))?; + let pool_entry = self.pool_entry(index)?; return match pool_entry { ConstantPoolInfo::NameAndType(data) => Ok(data), - _ => unreachable!(), + _ => Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type NameAndType but found {:?}", index, self.get_classname()?, pool_entry))) }; } @@ -319,7 +311,7 @@ impl FieldInfo { #[derive(Debug)] pub struct ConstantValueAttributeData { - constant_value_index: u16, + pub constant_value_index: u16, } #[derive(Debug)] @@ -970,13 +962,13 @@ pub fn read_u8(reader: &mut dyn Read) -> Result { return Ok(u8::from_be_bytes(u8_buffer)); } -fn pool_entry<'a>(pool: &Box<[ConstantPoolInfo]>, index: usize) -> Result<&ConstantPoolInfo, Error> { +fn pool_entry(constant_pool: &Box<[ConstantPoolInfo]>, index: u16) -> Result<&ConstantPoolInfo, Error> { if index == 0 { return Err(Error::BadFileError(format!("Bad pool index: 0"))); } - if index - 1 >= pool.len() { + if usize::from(index - 1) >= constant_pool.len() { return Err(Error::BadFileError(format!("Bad pool index: {}", index - 1))); } - return Ok(&pool[index - 1]); + return Ok(&constant_pool[usize::from(index - 1)]); } diff --git a/src/heap_area.rs b/src/heap_area.rs index 2345787..19b9f82 100644 --- a/src/heap_area.rs +++ b/src/heap_area.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use crate::accessmasks::FieldAccessFlag; -use crate::stackframe::StackValue; -use crate::classfile::{ JavaClassFile, AbstractTypeDescription, MethodInfo }; +use crate::classfile::{ JavaClassFile, AbstractTypeDescription, MethodInfo, AbstractTypeKind }; +use crate::jvm::Error; #[derive(Debug)] pub struct HeapArea { @@ -21,6 +21,10 @@ impl HeapArea { static_area: StaticArea::default(), } } + + pub fn make_static(&mut self, class: &JavaClassFile, class_index: usize) { + self.memory_used += self.static_area.make(class, class_index); + } } pub type ObjectReference=u32; @@ -56,18 +60,38 @@ pub struct StaticArea { } impl StaticArea { - pub fn make(&mut self, class: &JavaClassFile, class_index: usize) { + + pub fn set(&mut self, class_name: &String, field_name: &String, field_value: FieldValue) -> Result<(), Error> { + let static_object = match self.static_objects.get_mut(class_name) { + Some(o) => o, + None => return Err(Error::RunTimeError(format!("Trying to set '{}.{}={:?}' but there is no such static object.", class_name, field_name, field_value))), + }; + + let field_index = match static_object.field_index_from_name(&field_name) { + Some(i) => i, + None => return Err(Error::RunTimeError(format!("Trying to set '{}.{}={:?}' but there is no such static field on this object.", class_name, field_name, field_value))), + }; + + field_value.check_type(&static_object.fields[field_index].descriptor, class_name)?; + + static_object.fields[field_index].value = field_value; + + Ok(()) + } + + fn make(&mut self, class: &JavaClassFile, class_index: usize) -> usize { let mut fields = Vec::new(); + let mut fields_cumulative_size = 0; for field in &class.fields { if field.access_flags & FieldAccessFlag::Static { - fields.push( - StaticField { - name: field.name.clone(), - type_description: field.descriptor.clone(), - value: StackValue::default_for(field.descriptor), - } - ) + let new_field = StaticField { + name: field.name.clone(), + descriptor: field.descriptor.clone(), + value: FieldValue::default_for(&field.descriptor), + }; + fields_cumulative_size += std::mem::size_of_val(&new_field); + fields.push(new_field); } } @@ -77,7 +101,13 @@ impl StaticArea { methods: Box::new([]), }; + let object_memory_size = std::mem::size_of_val(&new_object); + 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 _ = self.static_objects.insert(class.get_classname().unwrap(), new_object); + + return object_memory_size + field_array_size + fields_cumulative_size + method_array_size; } } @@ -88,15 +118,69 @@ pub struct StaticObject { pub methods: Box<[MethodInfo]>, } +impl StaticObject { + fn field_index_from_name(&self, name: &String) -> Option { + for (index, field) in self.fields.iter().enumerate() { + if field.name == *name { + return Some(index) + } + } + return None; + } +} + #[derive(Debug)] pub struct StaticField { pub name: String, - pub type_description: AbstractTypeDescription, - pub value: StackValue, + pub descriptor: AbstractTypeDescription, + pub value: FieldValue, } #[derive(Debug)] pub struct ObjectField { pub type_description: AbstractTypeDescription, - pub value: StackValue, + pub value: FieldValue, +} + +#[derive(Debug)] +pub enum FieldValue { + Boolean(bool), + Byte(u8), + Char(u16), + Short(i16), + Int(i32), + Float(f32), + Reference(ObjectReference), + Double(f64), + Long(i64), +} + +impl FieldValue { + fn check_type(&self, t: &AbstractTypeDescription, class_name: &String) -> Result<(), Error> { + let default_val = FieldValue::default_for(t); + + return if std::mem::discriminant(&default_val) != std::mem::discriminant(self) { + Err(Error::RunTimeError(format!("Type Mismatch: trying to set {:?} on a field of type {:?} in class {}", self, default_val, class_name))) + } else { + Ok(()) + } + } + + fn default_for(t: &AbstractTypeDescription) -> Self { + if t.array_level != 0 { + return Self::Reference(0); + } + match &t.kind { + AbstractTypeKind::Void() => unreachable!(), + AbstractTypeKind::Byte() => Self::Byte(0), + AbstractTypeKind::Char() => Self::Char(0), + AbstractTypeKind::Double() => Self::Double(0.0), + AbstractTypeKind::Float() => Self::Float(0.0), + AbstractTypeKind::Int() => Self::Int(0), + AbstractTypeKind::Long() => Self::Long(0), + AbstractTypeKind::Classname(_) => Self::Reference(0), + AbstractTypeKind::Short() => Self::Short(0), + AbstractTypeKind::Boolean() => Self::Boolean(true), + } + } } diff --git a/src/jvm.rs b/src/jvm.rs index 2479d57..f2cae7c 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -3,14 +3,14 @@ use core::fmt::{Display, Formatter}; use std::collections::VecDeque; use std::error::Error as ErrorTrait; -use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag}; +use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag, FieldAccessFlag }; use crate::bytecode::{ Bytecode, Instruction }; use crate::classfile; -use crate::classfile::{ JavaClassFile, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData }; +use crate::classfile::{ JavaClassFile, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData }; use crate::classstore; use crate::classstore::ClassStore; use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo}; -use crate::heap_area::HeapArea; +use crate::heap_area::{ HeapArea, FieldValue }; use crate::stackframe; use crate::stackframe::{ StackFrame, StackValue, OperandStack }; @@ -173,10 +173,49 @@ impl JVM { let class_file = self.class_store.class_file_from_idx(class_idx).unwrap(); let clinit_idx = class_file.find_method_index(&"".to_string()); - // TODO: Static Stuff - self.heap_area.static_area.make(class_file, class_idx); + self.heap_area.make_static(class_file, class_idx); // TODO: ConstantValue Attributes (final) + for field in &class_file.fields { + if field.access_flags & FieldAccessFlag::Static { + let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter() + .filter(|a| match a.data { AttributeData::ConstantValue(_) => true, _ => false }) + .map(|a| match &a.data { AttributeData::ConstantValue(c) => c, _ => unreachable!() }) + .collect(); + + assert!(cvalue_attrs.len() < 2); + // TODO: Throw error + + if cvalue_attrs.len() == 1 { + let constant_value_info = cvalue_attrs[0]; + + assert!(field.descriptor.array_level == 0); + // TODO: Throw Error + + let field_value = match field.descriptor.kind { + AbstractTypeKind::Boolean() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; + + FieldValue::Boolean(int_entry.value != 0) + }, + AbstractTypeKind::Int() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; + + FieldValue::Int(int_entry.value) + }, + AbstractTypeKind::Short() => { + let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; + + FieldValue::Short(int_entry.value as i16) + }, + _ => todo!() + }; + + self.heap_area.static_area.set(&class_file.get_classname()?, &field.name, field_value)?; + } + } + } + // TODO: Push clinit function self.class_store.set_init(class_idx, true); diff --git a/src/stackframe.rs b/src/stackframe.rs index 21efbf4..64f1a39 100644 --- a/src/stackframe.rs +++ b/src/stackframe.rs @@ -1,4 +1,4 @@ -use crate::classfile::{ JavaClassFile, AttributeData, AbstractTypeDescription }; +use crate::classfile::{ JavaClassFile, AttributeData }; use crate::heap_area::ObjectReference; #[derive(Copy, Clone, Debug)] @@ -18,23 +18,6 @@ pub enum StackValue { Empty(), } -impl StackValue { - fn default_for(t: AbstractTypeDescription) -> Self { - match t { - AbstractTypeDescription::Void() => unreachable!(), - AbstractTypeDescription::Byte() => StackValue::Byte(0), - AbstractTypeDescription::Char() => StackValue::Char(0), - AbstractTypeDescription::Double() => StackValue::Double(0), - AbstractTypeDescription::Float() => , - AbstractTypeDescription::Int() => , - AbstractTypeDescription::Long() => , - AbstractTypeDescription::Classname(String) => , - AbstractTypeDescription::Short() => , - AbstractTypeDescription::Boolean() => , - } - } -} - #[derive(Debug)] pub struct OperandStack { stack: Box<[StackValue]>,