diff --git a/jclass/src/attributes.rs b/jclass/src/attributes.rs index f811a443..d3e9a4de 100644 --- a/jclass/src/attributes.rs +++ b/jclass/src/attributes.rs @@ -573,7 +573,8 @@ fn get_attribute( } "LocalVariableTable" => { let local_variable_table_length: u16 = get_int(&data, &mut start_from)?; - let mut local_variable_table = Vec::with_capacity(local_variable_table_length as usize); + let mut local_variable_table = + Vec::with_capacity(local_variable_table_length as usize); for _ in 0..local_variable_table_length { local_variable_table.push(LocalVariableTableRecord::new( get_int(&data, &mut start_from)?, diff --git a/jdescriptor/src/lib.rs b/jdescriptor/src/lib.rs index cd00ee81..c54815e5 100644 --- a/jdescriptor/src/lib.rs +++ b/jdescriptor/src/lib.rs @@ -30,13 +30,38 @@ pub enum TypeDescriptor { #[derive(Debug, PartialEq)] pub struct MethodDescriptor { parameter_types: Vec, + arguments_length: usize, return_type: TypeDescriptor, } +pub fn default_value(type_descriptor: &TypeDescriptor) -> Vec { + match type_descriptor { + Byte | Char | Int | Short | Boolean => vec![0], + Long => vec![0, 0], + Float => todo!(), + Double => todo!(), + Void => panic!("field can't be a void type"), + Array(_, _) => vec![0], + Object(_) => todo!(), + } +} + +pub fn get_length(type_descriptor: &TypeDescriptor) -> usize { + match type_descriptor { + Byte | Char | Int | Short | Boolean | Float => 1, + Long | Double => 2, + Void => panic!("field can't be a void type"), + Array(_, _) => 1, + Object(_) => todo!(), + } +} + impl MethodDescriptor { pub fn new(parameter_types: Vec, return_type: TypeDescriptor) -> Self { + let arguments_length = calculate_arguments_length(¶meter_types); Self { parameter_types, + arguments_length, return_type, } } @@ -45,11 +70,25 @@ impl MethodDescriptor { &self.parameter_types } + pub fn arguments_length(&self) -> usize { + self.arguments_length + } + pub fn return_type(&self) -> &TypeDescriptor { &self.return_type } } +fn calculate_arguments_length(parameter_types: &Vec) -> usize { + parameter_types + .iter() + .map(|t| match t { + Long | Double => 2, + _ => 1, + }) + .sum() +} + impl FromStr for TypeDescriptor { type Err = String; diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 001bbf22..27c20a83 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -7,6 +7,17 @@ fn should_do_adding() { assert_eq!(55, last_frame_value.unwrap()) } +#[test] +fn should_do_adding_with_longs() { + let vm = VM::new( + vec!["tests/test_data/AdderLong.class"], + "tests/test_data/std", + ) + .unwrap(); + let last_frame_value = vm.run("AdderLong").unwrap(); + assert_eq!(1_000_000_000, last_frame_value.unwrap()) +} + #[test] fn should_do_subtraction() { let vm = VM::new(vec!["tests/test_data/Sub.class"], "tests/test_data/std").unwrap(); @@ -14,6 +25,13 @@ fn should_do_subtraction() { assert_eq!(-999, last_frame_value.unwrap()) } +#[test] +fn should_do_subtraction_with_longs() { + let vm = VM::new(vec!["tests/test_data/SubLong.class"], "tests/test_data/std").unwrap(); + let last_frame_value = vm.run("SubLong").unwrap(); + assert_eq!(1_000_000_000, last_frame_value.unwrap()) +} + #[test] fn should_write_read_instance_fields() { let vm = VM::new( diff --git a/tests/test_data/AdderLong.class b/tests/test_data/AdderLong.class new file mode 100644 index 00000000..9a5e0535 Binary files /dev/null and b/tests/test_data/AdderLong.class differ diff --git a/tests/test_data/AdderLong.java b/tests/test_data/AdderLong.java new file mode 100644 index 00000000..7125f447 --- /dev/null +++ b/tests/test_data/AdderLong.java @@ -0,0 +1,13 @@ +//javac -g -parameters Adder.java + +public class AdderLong { + + public static void main(String[] args) { + long result = add(100_000_000_000L, -99_000_000_000L); + } + + private static long add(long a, long b) { + return a + b; + } + +} diff --git a/tests/test_data/ArrayLong.java b/tests/test_data/ArrayLong.java new file mode 100644 index 00000000..672ff496 --- /dev/null +++ b/tests/test_data/ArrayLong.java @@ -0,0 +1,120 @@ +public class Array { + + public static void main(String[] args) { + // 1. Initialize two arrays with different sizes + int[] numbers1 = {5, 3, 8, 12, 7}; + int[] numbers2 = {14, 22, 16, 9}; + + // 2. Combine two arrays dynamically + int[] combinedArray = combineArrays(numbers1, numbers2); + + // 3. Modify the combined array by squaring even numbers + squareEvenNumbers(combinedArray); + + // 4. Double the size of the combined array dynamically + combinedArray = resizeArray(combinedArray, combinedArray.length * 2); + + // 5. Fill the new half of the array with values + for (int i = combinedArray.length / 2; i < combinedArray.length; i++) { + combinedArray[i] = i * 2; // Fill new slots with multiples of 2 + } + + // 6. Find the second largest element in the combined array + int secondLargest = findSecondLargest(combinedArray); + + // 7. Reverse the combined array + reverseArray(combinedArray); + + + // 8. Shift array elements to the left by 11 positions + shiftLeft(combinedArray, 11); + + int result = combinedArray[0] + secondLargest; + } + + // Method to combine two arrays into one dynamically + private static int[] combineArrays(int[] array1, int[] array2) { + int newLength = array1.length + array2.length; + int[] result = new int[newLength]; + + // Copy elements from the first array + for (int i = 0; i < array1.length; i++) { + result[i] = array1[i]; + } + + // Copy elements from the second array + for (int i = 0; i < array2.length; i++) { + result[array1.length + i] = array2[i]; + } + + return result; + } + + // Method to square even numbers in the array + private static void squareEvenNumbers(int[] array) { + for (int i = 0; i < array.length; i++) { + if (array[i] % 2 == 0) { + array[i] = array[i] * array[i]; + } + } + } + + // Method to resize the array dynamically + private static int[] resizeArray(int[] array, int newSize) { + int[] newArray = new int[newSize]; + for (int i = 0; i < array.length; i++) { + newArray[i] = array[i]; + } + return newArray; + } + + // Method to find the second largest element in the array + private static int findSecondLargest(int[] array) { + int largest = Integer.MIN_VALUE; + int secondLargest = Integer.MIN_VALUE; + + for (int num : array) { + if (num > largest) { + secondLargest = largest; + largest = num; + } else if (num > secondLargest && num != largest) { + secondLargest = num; + } + } + return secondLargest; + } + + // Method to reverse the array in place + private static void reverseArray(int[] array) { + int start = 0; + int end = array.length - 1; + while (start < end) { + int temp = array[start]; + array[start] = array[end]; + array[end] = temp; + start++; + end--; + } + } + + // Method to shift the array elements to the left by a given number of positions + private static void shiftLeft(int[] array, int positions) { + int length = array.length; + int[] temp = new int[positions]; + + // Store the first 'positions' elements in a temporary array + for (int i = 0; i < positions; i++) { + temp[i] = array[i]; + } + + // Shift the remaining elements to the left + for (int i = positions; i < length; i++) { + array[i - positions] = array[i]; + } + + // Place the stored elements at the end + for (int i = 0; i < positions; i++) { + array[length - positions + i] = temp[i]; + } + } +} diff --git a/tests/test_data/SubLong.class b/tests/test_data/SubLong.class new file mode 100644 index 00000000..59a5c46f Binary files /dev/null and b/tests/test_data/SubLong.class differ diff --git a/tests/test_data/SubLong.java b/tests/test_data/SubLong.java new file mode 100644 index 00000000..0200a669 --- /dev/null +++ b/tests/test_data/SubLong.java @@ -0,0 +1,9 @@ + +public class SubLong { + private static long first = 100_000_000_000L; + private static long second = 99_000_000_000L; + + public static void main(String[] args) { + long result = first - second; + } +} diff --git a/vm/src/class_loader.rs b/vm/src/class_loader.rs index 26fd45f1..8185841f 100644 --- a/vm/src/class_loader.rs +++ b/vm/src/class_loader.rs @@ -8,6 +8,7 @@ use jclass::attributes::Attribute; use jclass::attributes::Attribute::Code; use jclass::class_file::{parse, ClassFile}; use jclass::fields::FieldFlags; +use jdescriptor::TypeDescriptor; use std::cell::RefCell; use std::collections::HashMap; use std::fs::File; @@ -65,10 +66,16 @@ impl ClassLoader { let class_name = Self::get_class_name(&class_file)?; let methods = Self::get_methods(&class_file, class_name.as_str())?; let static_fields = Self::get_static_fields(&class_file)?; + let non_static_fields_descriptors = Self::get_non_static_fields_descriptors(&class_file)?; Ok(( class_name.clone(), - JavaClass::new(methods, static_fields, class_file), + JavaClass::new( + methods, + static_fields, + non_static_fields_descriptors, + class_file, + ), )) } @@ -79,18 +86,22 @@ impl ClassLoader { for method in methods.iter() { let method_name = get_cpool_string(class_file, method.name_index() as usize) .ok_or(Error::new_constant_pool("Error getting method name"))?; - let method_signature = get_cpool_string(class_file, method.descriptor_index() as usize) - .ok_or(Error::new_constant_pool( - "Error getting method method_signature", - ))?; - let (max_stack, max_locals, code) = Self::get_cpool_code_attribute(method.attributes()) - .ok_or(Error::new_constant_pool("Error getting method code"))?; + let method_signature = + get_cpool_string(class_file, method.descriptor_index() as usize).ok_or( + Error::new_constant_pool("Error getting method method_signature"), + )?; + let (max_stack, max_locals, code) = + Self::get_cpool_code_attribute(method.attributes()) + .ok_or(Error::new_constant_pool("Error getting method code"))?; let key_signature = method_name + ":" + method_signature.as_str(); method_by_signature.insert( key_signature.clone(), JavaMethod::new( - method_signature.as_str().parse().map_err(|err| Error::new(ErrorKind::ClassFile(err)))?, + method_signature + .as_str() + .parse() + .map_err(|err| Error::new(ErrorKind::ClassFile(err)))?, max_stack, max_locals, code, @@ -112,14 +123,18 @@ impl ClassLoader { .ok_or_else(|| Error::new_constant_pool("Error getting field name")) .ok()?; - let _field_signature = + let field_signature = get_cpool_string(class_file, field.descriptor_index() as usize) .ok_or_else(|| { Error::new_constant_pool("Error getting field signature") }) .ok()?; - Some((field_name, RefCell::new(Field::new()))) + let result = str::parse::(field_signature.as_str()) + .map_err(|e| Error::new_constant_pool(e.as_str())) //todo: return proper error type? + .ok()?; + + Some((field_name, RefCell::new(Field::new(result)))) } else { None } @@ -129,6 +144,40 @@ impl ClassLoader { Ok(Fields::new(field_by_name)) } + // todo: this helper and `JavaClass.non_static_fields_descriptors` field are for PUTFIELD ops - refactor this approach + fn get_non_static_fields_descriptors( + class_file: &ClassFile, + ) -> Result> { + let non_static_fields_descriptors = class_file + .fields() + .iter() + .filter_map(|field| { + if !field.access_flags().contains(FieldFlags::ACC_STATIC) { + let field_name = get_cpool_string(class_file, field.name_index() as usize) + .ok_or_else(|| Error::new_constant_pool("Error getting field name")) + .ok()?; + + let field_signature = + get_cpool_string(class_file, field.descriptor_index() as usize) + .ok_or_else(|| { + Error::new_constant_pool("Error getting field signature") + }) + .ok()?; + + let result = str::parse::(field_signature.as_str()) + .map_err(|e| Error::new_constant_pool(e.as_str())) //todo: return proper error type? + .ok()?; + + Some((field_name, result)) + } else { + None + } + }) + .collect(); + + Ok(non_static_fields_descriptors) + } + fn get_cpool_code_attribute(attributes: &Vec) -> Option<(u16, u16, Vec)> { attributes.iter().find_map(|item| { if let Code { diff --git a/vm/src/execution_engine/engine.rs b/vm/src/execution_engine/engine.rs index 8b443645..c019d12d 100644 --- a/vm/src/execution_engine/engine.rs +++ b/vm/src/execution_engine/engine.rs @@ -5,7 +5,10 @@ use crate::heap::java_instance::JavaInstance; use crate::method_area::java_method::JavaMethod; use crate::method_area::method_area::MethodArea; use crate::stack::stack_frame::StackFrame; -use crate::util::{get_class_name_by_cpool_class_index, get_cpool_integer}; +use crate::util::{ + get_class_name_by_cpool_class_index, get_cpool_integer, get_cpool_long_double, Primitive, +}; +use jdescriptor::get_length; pub(crate) struct Engine<'a> { method_area: &'a MethodArea, @@ -56,6 +59,11 @@ impl<'a> Engine<'a> { stack_frame.incr_pc(); println!("ICONST_5"); } + LCONST_0 => { + stack_frame.push_i64(0i64); + stack_frame.incr_pc(); + println!("LCONST_0"); + } BIPUSH => { stack_frame.incr_pc(); let value = stack_frame.get_bytecode_byte() as i32; @@ -87,6 +95,27 @@ impl<'a> Engine<'a> { stack_frame.incr_pc(); println!("LDC -> cpoolindex={cpoolindex}, value={value}"); } + LDC2_W => { + let cpoolindex = Self::extract_two_bytes(stack_frame) as usize; + + let java_class = self + .method_area + .loaded_classes + .get(current_class_name.as_str()) + .unwrap(); + + let value = match get_cpool_long_double(&java_class.class_file, cpoolindex) { + Some(Primitive::Double(_v)) => todo!("add support for double"), + Some(Primitive::Long(v)) => { + stack_frame.push_i64(v); + v + } + _ => unreachable!(), + }; + + stack_frame.incr_pc(); + println!("LDC2_W -> cpoolindex={cpoolindex}, value={value}"); + } ILOAD => { stack_frame.incr_pc(); let pos = stack_frame.get_bytecode_byte() as usize; @@ -132,6 +161,30 @@ impl<'a> Engine<'a> { stack_frame.incr_pc(); println!("ILOAD_3 -> value={value}"); } + LLOAD_0 => { + let high = stack_frame.get_local(0); + let low = stack_frame.get_local(1); + + let value = ((high as i64) << 32) | (low as i64); + + stack_frame.push(low); + stack_frame.push(high); + + stack_frame.incr_pc(); + println!("LLOAD_0 -> value={value}"); + } + LLOAD_2 => { + let high = stack_frame.get_local(2); + let low = stack_frame.get_local(3); + + let value = ((high as i64) << 32) | (low as i64); + + stack_frame.push(low); + stack_frame.push(high); + + stack_frame.incr_pc(); + println!("LLOAD_2 -> value={value}"); + } ALOAD_0 => { let reference = stack_frame.get_local(0); stack_frame.push(reference); @@ -222,6 +275,17 @@ impl<'a> Engine<'a> { stack_frame.incr_pc(); println!("ISTORE_3 -> value={value}"); } + LSTORE_1 => { + let high = stack_frame.pop(); + let low = stack_frame.pop(); + + stack_frame.set_local(1, high); + stack_frame.set_local(2, low); + + stack_frame.incr_pc(); + let value = ((high as i64) << 32) | (low as i64); + println!("LSTORE_1 -> value={value}"); + } ASTORE_0 => { let objectref = stack_frame.pop(); stack_frame.set_local(0, objectref); @@ -304,6 +368,25 @@ impl<'a> Engine<'a> { stack_frame.incr_pc(); println!("IADD -> {a} + {b} = {result}"); } + LADD => { + let b_high = stack_frame.pop() as i64; + let b_low = stack_frame.pop() as i64; + + let a_high = stack_frame.pop() as i64; + let a_low = stack_frame.pop() as i64; + + let b = (b_high << 32) | b_low; + let a = (a_high << 32) | a_low; + let result = a + b; + + let result_low = result as i32; + let result_high = (result >> 32) as i32; + stack_frame.push(result_low); + stack_frame.push(result_high); + + stack_frame.incr_pc(); + println!("LADD -> {a} + {b} = {result}"); + } ISUB => { let b = stack_frame.pop(); let a = stack_frame.pop(); @@ -313,6 +396,25 @@ impl<'a> Engine<'a> { stack_frame.incr_pc(); println!("ISUB -> {a} - {b} = {result}"); } + LSUB => { + let b_high = stack_frame.pop() as i64; + let b_low = stack_frame.pop() as i64; + + let a_high = stack_frame.pop() as i64; + let a_low = stack_frame.pop() as i64; + + let b = (b_high << 32) | b_low; + let a = (a_high << 32) | a_low; + let result = a - b; + + let result_low = result as i32; + let result_high = (result >> 32) as i32; + stack_frame.push(result_low); + stack_frame.push(result_high); + + stack_frame.incr_pc(); + println!("LSUB -> {a} - {b} = {result}"); + } IMUL => { let b = stack_frame.pop(); let a = stack_frame.pop(); @@ -398,6 +500,21 @@ impl<'a> Engine<'a> { .push(ret); println!("IRETURN -> ret={ret}"); } + LRETURN => { + let ret_high = stack_frame.pop(); + let ret_low = stack_frame.pop(); + stack_frames.pop(); + let frame = stack_frames + .last_mut() + .ok_or(Error::new_execution("Error getting stack last value"))?; + + frame.push(ret_low); + frame.push(ret_high); + + let ret = ((ret_high as i64) << 32) | (ret_low as i64); + + println!("LRETURN -> ret={ret}"); + } ARETURN => { let objref = stack_frame.pop(); @@ -434,21 +551,27 @@ impl<'a> Engine<'a> { fieldref_constpool_index, )?; - let value = self + let field = self .method_area .loaded_classes .get(&class_name) .unwrap() - .fields + .static_fields .field_by_name .get(&field_name) .unwrap() - .borrow() - .value(); - stack_frame.push(value); + .borrow(); + field.raw_value().iter().for_each(|x| stack_frame.push(*x)); + + let value_as_string = field + .raw_value() + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","); + println!("GETSTATIC -> {class_name}.{field_name} is {value_as_string}"); stack_frame.incr_pc(); - println!("GETSTATIC -> {class_name}.{field_name} is {value}"); } PUTSTATIC => { let fieldref_constpool_index = Self::extract_two_bytes(stack_frame); @@ -465,13 +588,37 @@ impl<'a> Engine<'a> { fieldref_constpool_index, )?; - let value = stack_frame.pop(); + let len = { + let field = self + .method_area + .loaded_classes + .get(&class_name) + .unwrap() + .static_fields + .field_by_name + .get(&field_name) + .unwrap() + .borrow(); + + get_length(field.type_descriptor()) + }; + + let mut value = Vec::with_capacity(len); + for _ in 0..len { + value.push(stack_frame.pop()); + } + + let value_as_string = value + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","); self.method_area .set_static_field_value(&class_name, &field_name, value)?; + println!("PUTSTATIC -> {class_name}.{field_name} = {value_as_string}"); stack_frame.incr_pc(); - println!("PUTSTATIC -> {class_name}.{field_name} = {value}"); } GETFIELD => { let fieldref_constpool_index = Self::extract_two_bytes(stack_frame); @@ -493,10 +640,15 @@ impl<'a> Engine<'a> { .heap .get_object_field_value(objectref, field_name.as_str())?; - stack_frame.push(value); + value.iter().for_each(|x| stack_frame.push(*x)); stack_frame.incr_pc(); - println!("GETFIELD -> objectref={objectref}, class_name={class_name}, field_name={field_name}, value={value}"); + let value_as_string = value + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","); + println!("GETFIELD -> objectref={objectref}, class_name={class_name}, field_name={field_name}, value={value_as_string}"); } PUTFIELD => { let fieldref_constpool_index = Self::extract_two_bytes(stack_frame); @@ -512,14 +664,37 @@ impl<'a> Engine<'a> { java_class, fieldref_constpool_index, )?; - let value = stack_frame.pop(); + + let type_descriptor = self + .method_area + .loaded_classes + .get(&class_name) + .unwrap() + .non_static_fields_descriptors + .get(&field_name) + .unwrap(); + let len = get_length(type_descriptor); + + let mut value = Vec::with_capacity(len); + for _ in 0..len { + value.push(stack_frame.pop()); + } + let objectref = stack_frame.pop(); - self.heap - .set_object_field_value(objectref, field_name.as_str(), value)?; + self.heap.set_object_field_value( + objectref, + field_name.as_str(), + value.clone(), + )?; + let value_as_string = value + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","); + println!("PUTFIELD -> objectref={objectref}, class_name={class_name}, field_name={field_name} value={value_as_string}"); stack_frame.incr_pc(); - println!("PUTFIELD -> objectref={objectref}, class_name={class_name}, field_name={field_name} value={value}"); } INVOKEVIRTUAL => { let methodref_constpool_index = Self::extract_two_bytes(stack_frame); @@ -537,7 +712,7 @@ impl<'a> Engine<'a> { )?; let mut next_frame = virtual_method.new_stack_frame(); - let arg_num = virtual_method.get_signature().parameter_types().len(); + let arg_num = virtual_method.get_signature().arguments_length(); for i in (0..arg_num).rev() { let val = stack_frame.pop(); @@ -568,7 +743,7 @@ impl<'a> Engine<'a> { )?; let mut next_frame = special_method.new_stack_frame(); - let arg_num = special_method.get_signature().parameter_types().len(); + let arg_num = special_method.get_signature().arguments_length(); for i in (0..arg_num).rev() { let val = stack_frame.pop(); @@ -597,7 +772,7 @@ impl<'a> Engine<'a> { )?; let mut next_frame = static_method.new_stack_frame(); - let arg_num = static_method.get_signature().parameter_types().len(); + let arg_num = static_method.get_signature().arguments_length(); for i in (0..arg_num).rev() { let val = stack_frame.pop(); diff --git a/vm/src/heap/heap.rs b/vm/src/heap/heap.rs index ca33a789..58a8b759 100644 --- a/vm/src/heap/heap.rs +++ b/vm/src/heap/heap.rs @@ -29,7 +29,7 @@ impl<'a> Heap<'a> { &mut self, objectref: i32, fieldname: &str, - value: i32, + value: Vec, ) -> crate::error::Result<()> { if let Some(Object(instance)) = self.data.get_mut(&objectref) { instance.set_field_value(fieldname, value)?; @@ -43,7 +43,7 @@ impl<'a> Heap<'a> { &mut self, objectref: i32, fieldname: &str, - ) -> crate::error::Result { + ) -> crate::error::Result<&Vec> { if let Some(Object(java_instance)) = self.data.get(&objectref) { java_instance.get_field_value(fieldname) } else { diff --git a/vm/src/heap/java_instance.rs b/vm/src/heap/java_instance.rs index f7a7effb..21f41793 100644 --- a/vm/src/heap/java_instance.rs +++ b/vm/src/heap/java_instance.rs @@ -59,17 +59,21 @@ impl<'a> JavaInstance<'a> { }) } - pub fn set_field_value(&mut self, fieldname: &str, value: i32) -> crate::error::Result<()> { + pub fn set_field_value( + &mut self, + fieldname: &str, + value: Vec, + ) -> crate::error::Result<()> { self.fields .get_mut(fieldname) - .and_then(|v| Some(v.set_value(value))) + .and_then(|v| Some(v.set_raw_value(value))) .ok_or(Error::new_execution("error setting instance field value")) } - pub fn get_field_value(&self, fieldname: &str) -> crate::error::Result { + pub fn get_field_value(&self, fieldname: &str) -> crate::error::Result<&Vec> { self.fields .get(fieldname) - .and_then(|v| Some(v.value())) + .and_then(|v| Some(v.raw_value())) .ok_or(Error::new_execution("error getting instance field value")) } } diff --git a/vm/src/method_area/field.rs b/vm/src/method_area/field.rs index ff4dfbd0..7cc050ee 100644 --- a/vm/src/method_area/field.rs +++ b/vm/src/method_area/field.rs @@ -1,18 +1,29 @@ +use jdescriptor::{default_value, TypeDescriptor}; + #[derive(Debug)] pub(crate) struct Field { - value: i32, // todo: support other types + type_descriptor: TypeDescriptor, + value: Vec, } impl Field { - pub fn new() -> Self { - Self { value: 0 } + pub fn new(type_descriptor: TypeDescriptor) -> Self { + let value = default_value(&type_descriptor); + Self { + type_descriptor, + value, + } } - pub fn set_value(&mut self, value: i32) { + pub fn set_raw_value(&mut self, value: Vec) { self.value = value; } - pub fn value(&self) -> i32 { - self.value + pub fn raw_value(&self) -> &Vec { + &self.value + } + + pub fn type_descriptor(&self) -> &TypeDescriptor { + &self.type_descriptor } } diff --git a/vm/src/method_area/java_class.rs b/vm/src/method_area/java_class.rs index 758300f3..e3fb9cc1 100644 --- a/vm/src/method_area/java_class.rs +++ b/vm/src/method_area/java_class.rs @@ -1,13 +1,15 @@ use crate::method_area::field::Field; use crate::method_area::java_method::JavaMethod; use jclass::class_file::ClassFile; +use jdescriptor::TypeDescriptor; use std::cell::RefCell; use std::collections::HashMap; #[derive(Debug)] pub(crate) struct JavaClass { pub(crate) methods: Methods, - pub(crate) fields: Fields, + pub(crate) static_fields: Fields, + pub(crate) non_static_fields_descriptors: HashMap, pub(crate) class_file: ClassFile, } @@ -28,10 +30,16 @@ impl Fields { } impl JavaClass { - pub fn new(methods: Methods, fields: Fields, class_file: ClassFile) -> Self { + pub fn new( + methods: Methods, + static_fields: Fields, + non_static_fields_descriptors: HashMap, + class_file: ClassFile, + ) -> Self { Self { methods, - fields, + static_fields, + non_static_fields_descriptors, class_file, } } diff --git a/vm/src/method_area/method_area.rs b/vm/src/method_area/method_area.rs index 04c756f1..c8913a0e 100644 --- a/vm/src/method_area/method_area.rs +++ b/vm/src/method_area/method_area.rs @@ -19,13 +19,13 @@ impl MethodArea { &self, class_name: &str, fieldname: &str, - value: i32, + value: Vec, ) -> crate::error::Result<()> { self.loaded_classes .get(class_name) - .and_then(|java_class| java_class.fields.field_by_name.get(fieldname)) + .and_then(|java_class| java_class.static_fields.field_by_name.get(fieldname)) .and_then(|field| { - field.borrow_mut().set_value(value); + field.borrow_mut().set_raw_value(value); Some(()) }) diff --git a/vm/src/stack/stack_frame.rs b/vm/src/stack/stack_frame.rs index 30b7948e..532dc0bb 100644 --- a/vm/src/stack/stack_frame.rs +++ b/vm/src/stack/stack_frame.rs @@ -50,6 +50,14 @@ impl<'a> StackFrame<'a> { self.operand_stack.push(val); } + pub fn push_i64(&mut self, value: i64) { + let low = value as i32; + let high = (value >> 32) as i32; + + self.push(high); + self.push(low); + } + pub fn pop(&mut self) -> i32 { self.operand_stack.pop().unwrap() } diff --git a/vm/src/util.rs b/vm/src/util.rs index 98f8f81a..dbbd6b94 100644 --- a/vm/src/util.rs +++ b/vm/src/util.rs @@ -1,10 +1,16 @@ use crate::error::Error; use crate::method_area::field::Field; use jclass::class_file::ClassFile; +use jclass::constant_pool::ConstantPool; use jclass::constant_pool::ConstantPool::{Class, Integer, Utf8}; use jclass::fields::FieldFlags; use std::collections::HashMap; +pub(crate) enum Primitive { + Long(i64), + Double(f64), +} + pub(crate) fn get_class_name_by_cpool_class_index( class_index: usize, class_file: &ClassFile, @@ -37,6 +43,16 @@ pub(crate) fn get_cpool_integer(class_file: &ClassFile, index: usize) -> Option< }) } +pub(crate) fn get_cpool_long_double(class_file: &ClassFile, index: usize) -> Option { + let constant_pool = class_file.constant_pool(); + + constant_pool.get(index).and_then(|item| match item { + ConstantPool::Long { value } => Some(Primitive::Long(*value)), + ConstantPool::Double { value } => Some(Primitive::Double(*value)), + _ => todo!("symbolic reference to a dynamically-computed"), + }) +} + pub(crate) fn get_fields(class_file: &ClassFile) -> crate::error::Result> { let result = class_file .fields() @@ -48,12 +64,12 @@ pub(crate) fn get_fields(class_file: &ClassFile) -> crate::error::Result:()V") { - println!("About to initialize java class {class_name} java_method={java_method:?}"); + println!( + "About to initialize java class {class_name} java_method={java_method:?}" + ); engine.execute(java_method)?; //todo implement multiclass correct initialization order } }