Skip to content

Commit

Permalink
Add basic support of long
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Sep 17, 2024
1 parent 6260b3e commit 2548a60
Show file tree
Hide file tree
Showing 18 changed files with 523 additions and 50 deletions.
3 changes: 2 additions & 1 deletion jclass/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?,
Expand Down
39 changes: 39 additions & 0 deletions jdescriptor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,38 @@ pub enum TypeDescriptor {
#[derive(Debug, PartialEq)]
pub struct MethodDescriptor {
parameter_types: Vec<TypeDescriptor>,
arguments_length: usize,
return_type: TypeDescriptor,
}

pub fn default_value(type_descriptor: &TypeDescriptor) -> Vec<i32> {
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<TypeDescriptor>, return_type: TypeDescriptor) -> Self {
let arguments_length = calculate_arguments_length(&parameter_types);
Self {
parameter_types,
arguments_length,
return_type,
}
}
Expand All @@ -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<TypeDescriptor>) -> usize {
parameter_types
.iter()
.map(|t| match t {
Long | Double => 2,
_ => 1,
})
.sum()
}

impl FromStr for TypeDescriptor {
type Err = String;

Expand Down
18 changes: 18 additions & 0 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,31 @@ 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();
let last_frame_value = vm.run("Sub").unwrap();
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(
Expand Down
Binary file added tests/test_data/AdderLong.class
Binary file not shown.
13 changes: 13 additions & 0 deletions tests/test_data/AdderLong.java
Original file line number Diff line number Diff line change
@@ -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;
}

}
120 changes: 120 additions & 0 deletions tests/test_data/ArrayLong.java
Original file line number Diff line number Diff line change
@@ -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];
}
}
}
Binary file added tests/test_data/SubLong.class
Binary file not shown.
9 changes: 9 additions & 0 deletions tests/test_data/SubLong.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
69 changes: 59 additions & 10 deletions vm/src/class_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
),
))
}

Expand All @@ -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,
Expand All @@ -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::<TypeDescriptor>(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
}
Expand All @@ -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<HashMap<String, TypeDescriptor>> {
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::<TypeDescriptor>(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<Attribute>) -> Option<(u16, u16, Vec<u8>)> {
attributes.iter().find_map(|item| {
if let Code {
Expand Down
Loading

0 comments on commit 2548a60

Please sign in to comment.