More bytecode ops
This commit is contained in:
parent
9243c0b291
commit
5bc0d813e5
7 changed files with 395 additions and 120 deletions
155
src/bytecode.rs
155
src/bytecode.rs
|
@ -40,6 +40,7 @@ impl Bytecode {
|
|||
0x2C => (Instruction::LoadLocalReference2(), 1),
|
||||
0x2D => (Instruction::LoadLocalReference3(), 1),
|
||||
|
||||
0x32 => (Instruction::ArrayElement(), 1),
|
||||
0x3B => (Instruction::StoreLocalInt0(), 1),
|
||||
0x3C => (Instruction::StoreLocalInt1(), 1),
|
||||
0x3D => (Instruction::StoreLocalInt2(), 1),
|
||||
|
@ -59,7 +60,36 @@ impl Bytecode {
|
|||
|
||||
0x80 => (Instruction::OrInt(), 1),
|
||||
|
||||
0x99 => {
|
||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||
(Instruction::BranchZero(i16::from_be_bytes(bytes)), 3)
|
||||
}
|
||||
0x9A => {
|
||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||
(Instruction::BranchNonZero(i16::from_be_bytes(bytes)), 3)
|
||||
}
|
||||
0x9B => {
|
||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||
(Instruction::BranchNegative(i16::from_be_bytes(bytes)), 3)
|
||||
}
|
||||
0x9C => {
|
||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||
(Instruction::BranchNonPositive(i16::from_be_bytes(bytes)), 3)
|
||||
}
|
||||
0x9D => {
|
||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||
(Instruction::BranchPositive(i16::from_be_bytes(bytes)), 3)
|
||||
}
|
||||
0x9E => {
|
||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||
(Instruction::BranchNonNegative(i16::from_be_bytes(bytes)), 3)
|
||||
}
|
||||
|
||||
0xAC => (Instruction::ReturnInt(), 1),
|
||||
0xA7 => {
|
||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||
(Instruction::BranchAlways(i16::from_be_bytes(bytes)), 3)
|
||||
}
|
||||
|
||||
0xB0 => (Instruction::ReturnReference(), 1),
|
||||
0xB1 => (Instruction::ReturnVoid(), 1),
|
||||
|
@ -73,6 +103,7 @@ impl Bytecode {
|
|||
0xBA => (Instruction::InvokeDynamic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16, (self.bytes[offset+3] as u16) << 8 | self.bytes[offset+4] as u16), 5),
|
||||
0xBB => (Instruction::NewObject((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||
0xBD => (Instruction::NewArray((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||
0xBE => (Instruction::ArrayLength(), 1),
|
||||
_ => (Instruction::Unknown(opcode), 1)
|
||||
}
|
||||
}
|
||||
|
@ -104,68 +135,84 @@ impl Debug for Bytecode {
|
|||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum Instruction {
|
||||
NoOperation() = 0x00, // No-Operation
|
||||
StoreIntoIntArray() = 0x01, // ..., arrayref, index, value
|
||||
PushConstIntM1() = 0x02, // Push -1
|
||||
PushConstInt0() = 0x03, // Push 0
|
||||
PushConstInt1() = 0x04, // Push 1
|
||||
PushConstInt2() = 0x05, // Push 2
|
||||
PushConstInt3() = 0x06, // Push 3
|
||||
PushConstInt4() = 0x07, // Push 4
|
||||
PushConstInt5() = 0x08, // Push 5
|
||||
PushConstDouble0() = 0x0E, // Push 0.0
|
||||
PushConstDouble1() = 0x0F, // Push 1.0
|
||||
NoOperation() = 0x00, // No-Operation
|
||||
StoreIntoIntArray() = 0x01, // ..., arrayref, index, value
|
||||
PushConstIntM1() = 0x02, // Push -1
|
||||
PushConstInt0() = 0x03, // Push 0
|
||||
PushConstInt1() = 0x04, // Push 1
|
||||
PushConstInt2() = 0x05, // Push 2
|
||||
PushConstInt3() = 0x06, // Push 3
|
||||
PushConstInt4() = 0x07, // Push 4
|
||||
PushConstInt5() = 0x08, // Push 5
|
||||
PushConstDouble0() = 0x0E, // Push 0.0
|
||||
PushConstDouble1() = 0x0F, // Push 1.0
|
||||
|
||||
LoadByteImmediate(u8) = 0x10, // push immediate short
|
||||
LoadShortImmediate(u16) = 0x11, // push immediate short
|
||||
LoadConstant(u8) = 0x12, // Push from constant pool
|
||||
LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool
|
||||
LoadLocalInt0() = 0x1A, // Load int from local variable
|
||||
LoadLocalInt1() = 0x1B, // Load int from local variable
|
||||
LoadLocalInt2() = 0x1C, // Load int from local variable
|
||||
LoadLocalInt3() = 0x1D, // Load int from local variable
|
||||
LoadByteImmediate(u8) = 0x10, // push immediate short
|
||||
LoadShortImmediate(u16) = 0x11, // push immediate short
|
||||
LoadConstant(u8) = 0x12, // Push from constant pool
|
||||
LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool
|
||||
LoadLocalInt0() = 0x1A, // Load int from local variable
|
||||
LoadLocalInt1() = 0x1B, // Load int from local variable
|
||||
LoadLocalInt2() = 0x1C, // Load int from local variable
|
||||
LoadLocalInt3() = 0x1D, // Load int from local variable
|
||||
|
||||
LoadLocalDouble0() = 0x26, // Load local double variable reference onto stack
|
||||
LoadLocalDouble1() = 0x27, // Load local double variable reference onto stack
|
||||
LoadLocalDouble2() = 0x28, // Load local double variable reference onto stack
|
||||
LoadLocalDouble3() = 0x29, // Load local double variable reference onto stack
|
||||
LoadLocalReference0() = 0x2A, // Load local reference variable reference onto stack
|
||||
LoadLocalReference1() = 0x2B, // Load local reference variable reference onto stack
|
||||
LoadLocalReference2() = 0x2C, // Load local reference variable reference onto stack
|
||||
LoadLocalReference3() = 0x2D, // Load local reference variable reference onto stack
|
||||
LoadLocalDouble0() = 0x26, // Load local double variable reference onto stack
|
||||
LoadLocalDouble1() = 0x27, // Load local double variable reference onto stack
|
||||
LoadLocalDouble2() = 0x28, // Load local double variable reference onto stack
|
||||
LoadLocalDouble3() = 0x29, // Load local double variable reference onto stack
|
||||
LoadLocalReference0() = 0x2A, // Load local reference variable reference onto stack
|
||||
LoadLocalReference1() = 0x2B, // Load local reference variable reference onto stack
|
||||
LoadLocalReference2() = 0x2C, // Load local reference variable reference onto stack
|
||||
LoadLocalReference3() = 0x2D, // Load local reference variable reference onto stack
|
||||
|
||||
StoreLocalInt0() = 0x3B, // store int into local variable
|
||||
StoreLocalInt1() = 0x3C, // store int into local variable
|
||||
StoreLocalInt2() = 0x3D, // store int into local variable
|
||||
StoreLocalInt3() = 0x3E, // store int into local variable
|
||||
StoreReference0() = 0x4B, // store reference into local variable
|
||||
StoreReference1() = 0x4C, // store reference into local variable
|
||||
StoreReference2() = 0x4D, // store reference into local variable
|
||||
StoreReference3() = 0x4E, // store reference into local variable
|
||||
ArrayElement() = 0x32, // load element from array
|
||||
StoreLocalInt0() = 0x3B, // store int into local variable
|
||||
StoreLocalInt1() = 0x3C, // store int into local variable
|
||||
StoreLocalInt2() = 0x3D, // store int into local variable
|
||||
StoreLocalInt3() = 0x3E, // store int into local variable
|
||||
StoreReference0() = 0x4B, // store reference into local variable
|
||||
StoreReference1() = 0x4C, // store reference into local variable
|
||||
StoreReference2() = 0x4D, // store reference into local variable
|
||||
StoreReference3() = 0x4E, // store reference into local variable
|
||||
|
||||
Pop() = 0x57, // Pop top stack value
|
||||
Duplicate() = 0x59, // duplicate top stack value
|
||||
Pop() = 0x57, // Pop top stack value
|
||||
Duplicate() = 0x59, // duplicate top stack value
|
||||
|
||||
MultiplyInt() = 0x68, // int multiplication
|
||||
DivideLong() = 0x6D, // long division
|
||||
MultiplyInt() = 0x68, // int multiplication
|
||||
DivideLong() = 0x6D, // long division
|
||||
|
||||
ShiftIntRight() = 0x7a, // shift int
|
||||
ShiftIntRight() = 0x7a, // shift int
|
||||
|
||||
OrInt() = 0x80, // value, value => or
|
||||
OrInt() = 0x80, // value, value => or
|
||||
|
||||
ReturnInt() = 0xAC, // return integer from function
|
||||
BranchZero(i16) = 0x99, // branch if value == 0
|
||||
BranchNonZero(i16) = 0x9A, // branch if value != 0
|
||||
BranchNegative(i16) = 0x9B, // branch if value < 0
|
||||
BranchNonPositive(i16) = 0x9C, // branch if value <= 0
|
||||
BranchPositive(i16) = 0x9D, // branch if value > 0
|
||||
BranchNonNegative(i16) = 0x9E, // branch if value >= 0
|
||||
|
||||
ReturnReference() = 0xB0, // return top-ref from current function
|
||||
ReturnVoid() = 0xB1, // return void from function
|
||||
GetStatic(u16) = 0xB2, // get static field from class
|
||||
PutStatic(u16) = 0xB3, // set static field on class
|
||||
GetField(u16) = 0xB4, // get field from class
|
||||
PutField(u16) = 0xB5, // set field to a value
|
||||
InvokeVirtual(u16) = 0xB6, // invoke function on a class
|
||||
InvokeSpecial(u16) = 0xB7, // invoke instance method
|
||||
InvokeStatic(u16) = 0xB8, // invoke static function
|
||||
InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function
|
||||
NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference
|
||||
NewArray(u16) = 0xBD, // Create a new array from a constant-pool component class reference
|
||||
BranchIntEquality(i16) = 0x9F,
|
||||
BranchIntInequality(i16) = 0xA0,
|
||||
BranchIntLessThan(i16) = 0xA1,
|
||||
BranchIntGreaterEquals(i16) = 0xA2,
|
||||
BranchIntGreaterThan(i16) = 0xA3,
|
||||
|
||||
BranchAlways(i16) = 0xA7, // branch if true
|
||||
ReturnInt() = 0xAC, // return integer from function
|
||||
|
||||
ReturnReference() = 0xB0, // return top-ref from current function
|
||||
ReturnVoid() = 0xB1, // return void from function
|
||||
GetStatic(u16) = 0xB2, // get static field from class
|
||||
PutStatic(u16) = 0xB3, // set static field on class
|
||||
GetField(u16) = 0xB4, // get field from class
|
||||
PutField(u16) = 0xB5, // set field to a value
|
||||
InvokeVirtual(u16) = 0xB6, // invoke function on a class
|
||||
InvokeSpecial(u16) = 0xB7, // invoke instance method
|
||||
InvokeStatic(u16) = 0xB8, // invoke static function
|
||||
InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function
|
||||
NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference
|
||||
NewArray(u16) = 0xBD, // Create a new array from a constant-pool component class reference
|
||||
ArrayLength() = 0xBE, // Get length from array reference
|
||||
Unknown(u8),
|
||||
}
|
||||
|
|
|
@ -786,6 +786,14 @@ impl Into<String> for &AbstractTypeDescription {
|
|||
}
|
||||
|
||||
impl AbstractTypeDescription {
|
||||
pub fn parse_full(s: &str) -> Result<Self, Error> {
|
||||
let (c, parsed) = Self::parse_first(s)?;
|
||||
|
||||
assert!(c == s.len());
|
||||
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
pub fn parse_first(s: &str) -> Result<(usize, Self), Error> {
|
||||
let mut offset: usize = 0;
|
||||
let arrays_parsed = s.trim_start_matches("[");
|
||||
|
|
|
@ -168,6 +168,13 @@ impl ClassStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn class_name_from_index(&self, index: usize) -> Option<&String> {
|
||||
return match self.class_file_from_idx(index) {
|
||||
Some(file) => Some(file.get_classname().unwrap()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class_file_from_idx(&self, idx: usize) -> Option<&JavaClassFile> {
|
||||
return match self.classes.get(idx) {
|
||||
Some(entry) => Some(&entry.class_file),
|
||||
|
|
|
@ -200,15 +200,15 @@ impl ObjectArea {
|
|||
return class_store.get_native_class_name(native_name_index as usize);
|
||||
}
|
||||
|
||||
fn get_object_class_index(&self, reference: ObjectReference) -> usize {
|
||||
match self.get_object(reference) {
|
||||
pub fn get_object_class_index(&self, reference: ObjectReference) -> usize {
|
||||
match self.get_entry(reference) {
|
||||
CompartmentEntry::Object(o) => o.class_index,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_reference_class_ref(&self, reference: ObjectReference, class_store: &ClassStore) -> ObjectReference {
|
||||
match self.get_object(reference) {
|
||||
match self.get_entry(reference) {
|
||||
CompartmentEntry::Object(o) => class_store.get_class_objectref_from_index(o.class_index),
|
||||
CompartmentEntry::Array(a) => a.class_ref,
|
||||
_ => unreachable!(),
|
||||
|
@ -230,7 +230,7 @@ impl ObjectArea {
|
|||
return ObjectReference(object_index + (DEFAULT_COMPARTMENT_CAPACITY * (compartment_index as u32 + 1)));
|
||||
}
|
||||
|
||||
fn get_object(&self, reference: ObjectReference) -> &CompartmentEntry {
|
||||
fn get_entry(&self, reference: ObjectReference) -> &CompartmentEntry {
|
||||
let index = reference.0;
|
||||
let compartment_index: u32 = (index / DEFAULT_COMPARTMENT_CAPACITY) - 1;
|
||||
let object_index: u32 = index % DEFAULT_COMPARTMENT_CAPACITY;
|
||||
|
@ -241,7 +241,7 @@ impl ObjectArea {
|
|||
return object;
|
||||
}
|
||||
|
||||
fn get_object_mut(&mut self, reference: ObjectReference) -> &mut CompartmentEntry {
|
||||
fn get_entry_mut(&mut self, reference: ObjectReference) -> &mut CompartmentEntry {
|
||||
let index = reference.0;
|
||||
let compartment_index: u32 = (index / DEFAULT_COMPARTMENT_CAPACITY) - 1;
|
||||
let object_index: u32 = index % DEFAULT_COMPARTMENT_CAPACITY;
|
||||
|
@ -252,9 +252,29 @@ impl ObjectArea {
|
|||
return object;
|
||||
}
|
||||
|
||||
pub fn get_array_length(&self, reference: ObjectReference) -> usize {
|
||||
// TODO: Throw errors
|
||||
let array = match self.get_entry(reference) {
|
||||
CompartmentEntry::Array(a) => a,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
array.content.len()
|
||||
}
|
||||
|
||||
pub fn get_array_element(&self, array_ref: ObjectReference, element_index: i32) -> ObjectReference {
|
||||
// TODO: Throw errors
|
||||
let array = match self.get_entry(array_ref) {
|
||||
CompartmentEntry::Array(a) => a,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
array.content[element_index as usize]
|
||||
}
|
||||
|
||||
pub fn get_object_field(&self, reference: ObjectReference, field_name: &str, accessing_class_idx: usize, class_store: &ClassStore) -> Result<FieldValue, Error> {
|
||||
// TODO: Check access rights
|
||||
let object = match self.get_object(reference) {
|
||||
let object = match self.get_entry(reference) {
|
||||
CompartmentEntry::Object(o) => o,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
@ -282,7 +302,7 @@ impl ObjectArea {
|
|||
}
|
||||
|
||||
pub fn set_array_element(&mut self, array_reference: ObjectReference, index: usize, element: ObjectReference) {
|
||||
let array = match self.get_object_mut(array_reference) {
|
||||
let array = match self.get_entry_mut(array_reference) {
|
||||
CompartmentEntry::Array(a) => a,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
@ -294,7 +314,7 @@ impl ObjectArea {
|
|||
|
||||
pub fn set_object_field(&mut self, reference: ObjectReference, field_name: &str, value: FieldValue, accessing_class_idx: usize, class_store: &ClassStore) -> Result<(), Error> {
|
||||
// TODO: Check access rights
|
||||
let object = match self.get_object_mut(reference) {
|
||||
let object = match self.get_entry_mut(reference) {
|
||||
CompartmentEntry::Object(o) => o,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
@ -425,6 +445,26 @@ pub struct StaticArea {
|
|||
|
||||
impl StaticArea {
|
||||
|
||||
pub fn get(&self, class_name: &String, field_name: &String, expected_field_descriptor: AbstractTypeDescription) -> Result<FieldValue, Error> {
|
||||
// TODO: Access permission checking
|
||||
let static_object = match self.static_objects.get(class_name) {
|
||||
Some(o) => o,
|
||||
None => return Err(Error::RunTimeError(format!("Trying to get '{}.{}' but there is no such static object.", class_name, field_name))),
|
||||
};
|
||||
|
||||
let field_index = match static_object.field_index_from_name(&field_name) {
|
||||
Some(i) => i,
|
||||
None => return Err(Error::RunTimeError(format!("Trying to get '{}.{}' but there is no such static field on this object.", class_name, field_name))),
|
||||
};
|
||||
|
||||
let field_descriptor = &static_object.fields[field_index].descriptor;
|
||||
if expected_field_descriptor != *field_descriptor {
|
||||
return Err(Error::RunTimeError(format!("Descriptor Mismatch: Trying to get a {expected_field_descriptor:?} from '{class_name}.{field_name} but the field is of type '{field_descriptor:?}'.")));
|
||||
}
|
||||
|
||||
Ok(static_object.fields[field_index].value)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, class_name: &String, field_name: &String, field_value: FieldValue) -> Result<(), Error> {
|
||||
// TODO: Access permission checking
|
||||
let static_object = match self.static_objects.get_mut(class_name) {
|
||||
|
|
254
src/jvm.rs
254
src/jvm.rs
|
@ -194,7 +194,7 @@ impl JVM {
|
|||
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 8, descriptor_index: 9 }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: class_name.to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: method_name.to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "()V".to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "([Ljava/lang/String;)V".to_string() }),
|
||||
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 11, name_and_type_index: 13}), // 10
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 12 } ),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/String".to_string() }),
|
||||
|
@ -227,10 +227,12 @@ impl JVM {
|
|||
attribute_name_index: 3,
|
||||
data: AttributeData::Code(
|
||||
CodeAttributeData {
|
||||
max_stack: 0,
|
||||
max_stack: 1,
|
||||
max_locals: 1,
|
||||
code: Bytecode {
|
||||
bytes: Box::new([
|
||||
0x2A_u8.to_be(), // aload_0
|
||||
|
||||
0xB8_u8.to_be(), // invokestatic
|
||||
0x04_u16.to_be_bytes()[0], // index 4 into the constant
|
||||
0x04_u16.to_be_bytes()[1], // pool
|
||||
|
@ -243,54 +245,12 @@ impl JVM {
|
|||
}
|
||||
])
|
||||
},
|
||||
MethodInfo {
|
||||
access_flags: MethodAccessFlagMask {
|
||||
mask: MethodAccessFlag::Public.discriminant() | MethodAccessFlag::Static.discriminant()
|
||||
},
|
||||
name: "make_arg_string".to_string(),
|
||||
descriptor: MethodDescriptor {
|
||||
argument_types: Box::new([]),
|
||||
return_type: AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Void(),
|
||||
}
|
||||
},
|
||||
code_attribute_index: 0,
|
||||
attributes: Box::new([
|
||||
AttributeInfo {
|
||||
attribute_name_index: 3,
|
||||
data: AttributeData::Code(
|
||||
CodeAttributeData {
|
||||
max_stack: 3,
|
||||
max_locals: 3,
|
||||
code: Bytecode {
|
||||
bytes: Box::new([
|
||||
0xBB_u8.to_be(), // new
|
||||
11_u16.to_be_bytes()[0], // index 11 into the constant
|
||||
11_u16.to_be_bytes()[1], // pool
|
||||
|
||||
0x59_u8.to_be(), // dup
|
||||
|
||||
0x2A_u8.to_be(), // aload 0
|
||||
|
||||
0xB7_u8.to_be(), // invokespecial
|
||||
10_u16.to_be_bytes()[0], // index 10 into the constant
|
||||
10_u16.to_be_bytes()[1], // pool
|
||||
])
|
||||
},
|
||||
exception_table: Box::new([]),
|
||||
attributes: Box::new([]),
|
||||
}
|
||||
)
|
||||
}
|
||||
])
|
||||
}
|
||||
]),
|
||||
attributes: Box::new([]),
|
||||
};
|
||||
|
||||
self.class_store.add_class(entry_class, true)?; // 0
|
||||
self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1
|
||||
self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1
|
||||
self.class_store.load_class(&"java/lang/Object".to_string())?; // 2
|
||||
self.class_store.load_class(&"java/lang/Number".to_string())?; // 3
|
||||
self.class_store.load_class(&"java/lang/Byte".to_string())?; // 4
|
||||
|
@ -537,13 +497,100 @@ impl JVM {
|
|||
|
||||
match instruction {
|
||||
|
||||
Instruction::ArrayLength() => {
|
||||
let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
let array_length = self.heap_area.object_area.get_array_length(array_reference);
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(array_length as i32)))?;
|
||||
},
|
||||
|
||||
Instruction::ArrayElement() => {
|
||||
let element_index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
let element = self.heap_area.object_area.get_array_element(array_reference, element_index);
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(element)))?;
|
||||
},
|
||||
|
||||
Instruction::BranchAlways(branch_offset) => {
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
||||
}
|
||||
|
||||
Instruction::BranchZero(branch_offset) => {
|
||||
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
if test_value == 0 {
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
||||
}
|
||||
}
|
||||
|
||||
Instruction::Duplicate() => {
|
||||
let popped_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
||||
}
|
||||
|
||||
Instruction::GetField(fieldref_index) => {
|
||||
let (class_name, field_name, field_descriptor) = class.gather_fieldref(fieldref_index).unwrap();
|
||||
|
||||
let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
// TODO: Are there any methods callable on arrays?
|
||||
let this_object_class_index = self.heap_area.object_area.get_object_class_index(this_object);
|
||||
let this_object_class_name = self.class_store.class_name_from_index(this_object_class_index).unwrap().to_string();
|
||||
let this_object_descriptor = AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Classname(this_object_class_name.clone())
|
||||
};
|
||||
let object_descriptor = AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Classname(class_name.to_string())
|
||||
};
|
||||
|
||||
if ! self.class_store.are_types_compatible(&this_object_descriptor, &object_descriptor) {
|
||||
return Err(Error::RunTimeError(format!(
|
||||
"GetField: Cannot fetch '{field_name}' from class '{class_name}' on instance from class '{}: Types are incompatible'",
|
||||
this_object_class_name
|
||||
)))
|
||||
}
|
||||
|
||||
let fetched_value = self.heap_area.object_area.get_object_field(this_object, field_name, frame.class_index, &self.class_store).unwrap();
|
||||
|
||||
// println!("{fetched_value:?} {field_descriptor}");
|
||||
// TODO: Check field and value compatability
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(fetched_value))?;
|
||||
}
|
||||
|
||||
Instruction::GetStatic(fieldref_index) => {
|
||||
let (class_name, field_name, field_descriptor) = class.gather_fieldref(fieldref_index).unwrap();
|
||||
|
||||
if ! self.class_store.have_class(class_name) {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
||||
}
|
||||
if ! self.class_store.was_init(class_name).unwrap() {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
return Ok(JVMCallbackOperation::InitClass(class_name.to_string()));
|
||||
}
|
||||
|
||||
// TODO: Throw error
|
||||
let parsed_field_descriptor = AbstractTypeDescription::parse_full(field_descriptor).unwrap();
|
||||
|
||||
// TODO: Throw error
|
||||
let fetched_value = self.heap_area.static_area.get(class_name, field_name, parsed_field_descriptor).unwrap();
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(fetched_value))?;
|
||||
}
|
||||
|
||||
Instruction::InvokeSpecial(methodref_index) => {
|
||||
// No instance-based dispatch
|
||||
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?;
|
||||
|
||||
if ! self.class_store.have_class(supplied_class_name) {
|
||||
|
@ -654,6 +701,77 @@ impl JVM {
|
|||
return Ok(JVMCallbackOperation::PushFrame(new_frame));
|
||||
},
|
||||
|
||||
Instruction::InvokeVirtual(methodref_index) => {
|
||||
let (base_class_name, base_method_name, base_descriptor_string) = class.gather_methodref(methodref_index)?;
|
||||
|
||||
if ! self.class_store.have_class(base_class_name) {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
return Ok(JVMCallbackOperation::LoadClass(base_class_name.to_string()));
|
||||
}
|
||||
if ! self.class_store.was_init(base_class_name).unwrap() {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
return Ok(JVMCallbackOperation::InitClass(base_class_name.to_string()));
|
||||
}
|
||||
|
||||
let base_descriptor = MethodDescriptor::try_from(base_descriptor_string).unwrap();
|
||||
|
||||
|
||||
// extract arguments, they are on top of the stack
|
||||
let mut arguments = VecDeque::new();
|
||||
fill_arguments(class, method, &mut arguments, &base_descriptor.argument_types, &mut frame.operand_stack)?;
|
||||
let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
arguments.push_front(StackValue::Reference(this_object));
|
||||
|
||||
// TODO: Are there any methods callable on arrays?
|
||||
let this_object_class_index = self.heap_area.object_area.get_object_class_index(this_object);
|
||||
let this_object_class_name = self.class_store.class_name_from_index(this_object_class_index).unwrap().to_string();
|
||||
let this_object_descriptor = AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Classname(this_object_class_name.clone())
|
||||
};
|
||||
let base_object_descriptor = AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Classname(base_class_name.to_string())
|
||||
};
|
||||
|
||||
|
||||
// TODO: Throw error
|
||||
if ! self.class_store.are_types_compatible(&this_object_descriptor, &base_object_descriptor) {
|
||||
return Err(Error::RunTimeError(format!(
|
||||
"InvokeVirtual: Cannot call '{base_method_name}' from class '{base_class_name}' on instance from class '{}: Types are incompatible'",
|
||||
this_object_class_name
|
||||
)))
|
||||
}
|
||||
|
||||
let (invoked_class_index, invoked_method_index, _invoked_method_info) = match ClassMethodIterator::new(this_object_class_index, &self.class_store)
|
||||
.filter(|(_, _, method_info)| method_info.name == *base_method_name)
|
||||
.filter(|(_, _, method_info)| method_info.descriptor == base_descriptor)
|
||||
.next()
|
||||
{
|
||||
Some(t) => t,
|
||||
None => {
|
||||
return Err(Error::RunTimeError(format!(
|
||||
"InvokeVirtual: Failed to find requested method '{}' with descriptor '{}' in the type hierarchy of '{}'",
|
||||
base_method_name,
|
||||
base_descriptor_string,
|
||||
this_object_class_name,
|
||||
)));
|
||||
},
|
||||
};
|
||||
|
||||
let invoked_class_file = self.class_store.class_file_from_idx(invoked_class_index).unwrap();
|
||||
let new_frame = StackFrame::new(
|
||||
invoked_class_file,
|
||||
invoked_class_index,
|
||||
invoked_method_index as u16,
|
||||
arguments.make_contiguous()
|
||||
);
|
||||
|
||||
return Ok(JVMCallbackOperation::PushFrame(new_frame));
|
||||
},
|
||||
|
||||
Instruction::LoadByteImmediate(byte) => {
|
||||
// sign extend into int
|
||||
let i8_int = i8::from_be_bytes([byte]);
|
||||
|
@ -681,6 +799,15 @@ impl JVM {
|
|||
Instruction::LoadLocalReference0() => {
|
||||
load_local_reference(class, method, frame, 0)?;
|
||||
}
|
||||
Instruction::LoadLocalReference1() => {
|
||||
load_local_reference(class, method, frame, 1)?;
|
||||
}
|
||||
Instruction::LoadLocalReference2() => {
|
||||
load_local_reference(class, method, frame, 2)?;
|
||||
}
|
||||
Instruction::LoadLocalReference3() => {
|
||||
load_local_reference(class, method, frame, 3)?;
|
||||
}
|
||||
|
||||
Instruction::MultiplyInt() => {
|
||||
let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
@ -834,12 +961,9 @@ impl JVM {
|
|||
}
|
||||
|
||||
Instruction::ReturnInt() => {
|
||||
let expected_type = AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Int(),
|
||||
};
|
||||
if method.descriptor.return_type != expected_type {
|
||||
return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
||||
match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) {
|
||||
(_, AbstractTypeKind::Byte() | AbstractTypeKind::Boolean()| AbstractTypeKind::Int() | AbstractTypeKind::Char() | AbstractTypeKind::Short()) => (),
|
||||
_ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
||||
}
|
||||
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
@ -859,6 +983,13 @@ impl JVM {
|
|||
return Ok(JVMCallbackOperation::PopFrame());
|
||||
},
|
||||
|
||||
Instruction::ShiftIntRight() => {
|
||||
let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 & 0b00011111;
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int >> shift)))?;
|
||||
},
|
||||
|
||||
Instruction::StoreLocalInt0() => {
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
|
@ -880,6 +1011,27 @@ impl JVM {
|
|||
wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Int(int)))?;
|
||||
},
|
||||
|
||||
Instruction::StoreReference0() => {
|
||||
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Reference(reference)))?;
|
||||
},
|
||||
Instruction::StoreReference1() => {
|
||||
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Reference(reference)))?;
|
||||
},
|
||||
Instruction::StoreReference2() => {
|
||||
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Reference(reference)))?;
|
||||
},
|
||||
Instruction::StoreReference3() => {
|
||||
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Reference(reference)))?;
|
||||
},
|
||||
|
||||
_ => {
|
||||
return Err(Error::RunTimeError(format!("Opcode not implemented yet: {:?}", instruction)))
|
||||
},
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -20,18 +20,18 @@ fn main() {
|
|||
let mut jvm = jvm::JVM::new();
|
||||
|
||||
match jvm.entrypoint(
|
||||
&"java/Math".to_string(),
|
||||
&"v".to_string(),
|
||||
&"Main".to_string(),
|
||||
&"main".to_string(),
|
||||
&["Hello World"],
|
||||
) {
|
||||
Ok(()) => (),
|
||||
Err(e) => println!("{:#?}", e),
|
||||
}
|
||||
|
||||
// match jvm.run() {
|
||||
// Ok(()) => (),
|
||||
// Err(e) => println!("{:#?}", e),
|
||||
// };
|
||||
match jvm.run() {
|
||||
Ok(()) => (),
|
||||
Err(e) => println!("{:#?}", e),
|
||||
};
|
||||
|
||||
//println!("{:#?}", jvm.heap_area.static_area);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::classfile::{ JavaClassFile, AttributeData };
|
||||
use crate::heap_area::ObjectReference;
|
||||
use crate::heap_area::{ ObjectReference, FieldValue };
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum StackValue {
|
||||
|
@ -48,6 +48,27 @@ impl OperandStack {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push_field_value(&mut self, value: FieldValue) -> Result<(), Error> {
|
||||
match value {
|
||||
FieldValue::Reference(r) => {
|
||||
self.push(StackValue::Reference(r))
|
||||
}
|
||||
|
||||
FieldValue::Boolean(b) => {
|
||||
self.push(StackValue::Int(if b { 1 } else { 0 }))
|
||||
}
|
||||
|
||||
FieldValue::Byte(b) => {
|
||||
self.push(StackValue::Int(b as i32))
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("{value:?}");
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_computational_1(&mut self, index: usize) -> Result<StackValue, Error> {
|
||||
let absolute_index = self.depth as usize - 1 - index;
|
||||
|
|
Loading…
Reference in a new issue