Compare commits
2 commits
b4c428685f
...
0c54a1d7e1
Author | SHA1 | Date | |
---|---|---|---|
0c54a1d7e1 | |||
64eef60c4e |
8 changed files with 1887 additions and 81 deletions
|
@ -58,6 +58,7 @@ impl Bytecode {
|
||||||
0x57 => (Instruction::Pop(), 1),
|
0x57 => (Instruction::Pop(), 1),
|
||||||
0x59 => (Instruction::Duplicate(), 1),
|
0x59 => (Instruction::Duplicate(), 1),
|
||||||
|
|
||||||
|
0x60 => (Instruction::AddInt(), 1),
|
||||||
0x68 => (Instruction::MultiplyInt(), 1),
|
0x68 => (Instruction::MultiplyInt(), 1),
|
||||||
0x6C => (Instruction::DivideInt(), 1),
|
0x6C => (Instruction::DivideInt(), 1),
|
||||||
0x6D => (Instruction::DivideLong(), 1),
|
0x6D => (Instruction::DivideLong(), 1),
|
||||||
|
@ -80,7 +81,7 @@ impl Bytecode {
|
||||||
}
|
}
|
||||||
0x9C => {
|
0x9C => {
|
||||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||||
(Instruction::BranchNonPositive(i16::from_be_bytes(bytes)), 3)
|
(Instruction::BranchNonNegative(i16::from_be_bytes(bytes)), 3)
|
||||||
}
|
}
|
||||||
0x9D => {
|
0x9D => {
|
||||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||||
|
@ -88,7 +89,7 @@ impl Bytecode {
|
||||||
}
|
}
|
||||||
0x9E => {
|
0x9E => {
|
||||||
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
|
||||||
(Instruction::BranchNonNegative(i16::from_be_bytes(bytes)), 3)
|
(Instruction::BranchNonPositive(i16::from_be_bytes(bytes)), 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
0x9F => {
|
0x9F => {
|
||||||
|
@ -194,7 +195,7 @@ pub enum Instruction {
|
||||||
LoadByteImmediate(u8) = 0x10, // push immediate short
|
LoadByteImmediate(u8) = 0x10, // push immediate short
|
||||||
LoadShortImmediate(u16) = 0x11, // push immediate short
|
LoadShortImmediate(u16) = 0x11, // push immediate short
|
||||||
LoadConstant(u8) = 0x12, // Push from constant pool
|
LoadConstant(u8) = 0x12, // Push from constant pool
|
||||||
LoadCostantWide(u16) = 0x13, // Push from constant pool with wide index, don't load
|
LoadCostantWide(u16) = 0x13, // Push from constant pool with wide index, don't load
|
||||||
// double or long or whatever
|
// double or long or whatever
|
||||||
LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool
|
LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool
|
||||||
LoadLocalInt0() = 0x1A, // Load int from local variable
|
LoadLocalInt0() = 0x1A, // Load int from local variable
|
||||||
|
@ -227,6 +228,7 @@ pub enum Instruction {
|
||||||
Pop() = 0x57, // Pop top stack value
|
Pop() = 0x57, // Pop top stack value
|
||||||
Duplicate() = 0x59, // duplicate top stack value
|
Duplicate() = 0x59, // duplicate top stack value
|
||||||
|
|
||||||
|
AddInt() = 0x60, // int addition
|
||||||
MultiplyInt() = 0x68, // int multiplication
|
MultiplyInt() = 0x68, // int multiplication
|
||||||
DivideInt() = 0x6C, // integer division, round toward zero and more rules
|
DivideInt() = 0x6C, // integer division, round toward zero and more rules
|
||||||
DivideLong() = 0x6D, // long division
|
DivideLong() = 0x6D, // long division
|
||||||
|
@ -238,9 +240,9 @@ pub enum Instruction {
|
||||||
BranchZero(i16) = 0x99, // branch if value == 0
|
BranchZero(i16) = 0x99, // branch if value == 0
|
||||||
BranchNonZero(i16) = 0x9A, // branch if value != 0
|
BranchNonZero(i16) = 0x9A, // branch if value != 0
|
||||||
BranchNegative(i16) = 0x9B, // branch if value < 0
|
BranchNegative(i16) = 0x9B, // branch if value < 0
|
||||||
BranchNonPositive(i16) = 0x9C, // branch if value <= 0
|
BranchNonNegative(i16) = 0x9C, // branch if value <= 0
|
||||||
BranchPositive(i16) = 0x9D, // branch if value > 0
|
BranchPositive(i16) = 0x9D, // branch if value > 0
|
||||||
BranchNonNegative(i16) = 0x9E, // branch if value >= 0
|
BranchNonPositive(i16) = 0x9E, // branch if value >= 0
|
||||||
|
|
||||||
BranchIntEquality(i16) = 0x9F,
|
BranchIntEquality(i16) = 0x9F,
|
||||||
BranchIntInequality(i16) = 0xA0,
|
BranchIntInequality(i16) = 0xA0,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use core::str::Utf8Error;
|
||||||
|
|
||||||
use crate::accessmasks::*;
|
use crate::accessmasks::*;
|
||||||
use crate::bytecode::Bytecode;
|
use crate::bytecode::Bytecode;
|
||||||
use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantStringInfo, ConstantMethodRefInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo };
|
use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantStringInfo, ConstantMethodRefInfo, ConstantFloatInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo };
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -246,6 +246,15 @@ impl JavaClassFile {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pool_float_entry(&self, index: u16) -> Result<&ConstantFloatInfo, Error> {
|
||||||
|
let pool_entry = self.pool_entry(index)?;
|
||||||
|
|
||||||
|
return match pool_entry {
|
||||||
|
ConstantPoolInfo::Float(data) => Ok(data),
|
||||||
|
_ => Err(Error::BadFileError(format!("Expected constant pool entry {} in class {} to be of type Float but found {:?}", index, self.get_classname()?, pool_entry)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pool_long_entry(&self, index: u16) -> Result<&ConstantLongInfo, Error> {
|
pub fn pool_long_entry(&self, index: u16) -> Result<&ConstantLongInfo, Error> {
|
||||||
let pool_entry = self.pool_entry(index)?;
|
let pool_entry = self.pool_entry(index)?;
|
||||||
|
|
||||||
|
@ -282,6 +291,12 @@ impl JavaClassFile {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gather_float(&self, index: u16) -> Result<f32, Error> {
|
||||||
|
let float = self.pool_float_entry(index)?;
|
||||||
|
|
||||||
|
return Ok(float.value);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gather_string(&self, index: u16) -> Result<&String, Error> {
|
pub fn gather_string(&self, index: u16) -> Result<&String, Error> {
|
||||||
let string = self.pool_string_entry(index)?;
|
let string = self.pool_string_entry(index)?;
|
||||||
|
|
||||||
|
@ -766,17 +781,17 @@ impl Into<String> for &AbstractTypeKind {
|
||||||
impl From<&str> for AbstractTypeKind {
|
impl From<&str> for AbstractTypeKind {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
match value.chars().nth(0).unwrap() {
|
match value.chars().nth(0).unwrap() {
|
||||||
'V' => AbstractTypeKind::Void(),
|
'V' => AbstractTypeKind::Void(),
|
||||||
'B' => AbstractTypeKind::Byte(),
|
'B' => AbstractTypeKind::Byte(),
|
||||||
'C' => AbstractTypeKind::Char(),
|
'C' => AbstractTypeKind::Char(),
|
||||||
'D' => AbstractTypeKind::Double(),
|
'D' => AbstractTypeKind::Double(),
|
||||||
'F' => AbstractTypeKind::Float(),
|
'F' => AbstractTypeKind::Float(),
|
||||||
'I' => AbstractTypeKind::Int(),
|
'I' => AbstractTypeKind::Int(),
|
||||||
'J' => AbstractTypeKind::Long(),
|
'J' => AbstractTypeKind::Long(),
|
||||||
'S' => AbstractTypeKind::Short(),
|
'S' => AbstractTypeKind::Short(),
|
||||||
'Z' => AbstractTypeKind::Boolean(),
|
'Z' => AbstractTypeKind::Boolean(),
|
||||||
'L' => todo!(),
|
'L' => AbstractTypeKind::Classname(value.chars().skip(1).map_while(|c| if c != ';' { Some(c) } else { None } ).collect()),
|
||||||
_ => todo!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,24 +12,24 @@ use crate::iterators::CompatibleTypesIterator;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ClassStore {
|
pub struct ClassStore {
|
||||||
class_ids: HashMap<String, usize>,
|
pub class_ids: HashMap<String, usize>,
|
||||||
array_classes: HashMap<AbstractTypeDescription, ObjectReference>,
|
pub array_classes: HashMap<AbstractTypeDescription, ObjectReference>,
|
||||||
classes: Vec<ClassStoreEntry>,
|
pub classes: Vec<ClassStoreEntry>,
|
||||||
class_path_fragments: Vec<PathBuf>,
|
pub class_path_fragments: Vec<PathBuf>,
|
||||||
native_class_names: Vec<String>,
|
pub native_class_names: Vec<String>,
|
||||||
primitive_classes: PrimitiveClassStore,
|
pub primitive_classes: PrimitiveClassStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct PrimitiveClassStore {
|
pub struct PrimitiveClassStore {
|
||||||
byte_class: ObjectReference,
|
pub byte_class: ObjectReference,
|
||||||
char_class: ObjectReference,
|
pub char_class: ObjectReference,
|
||||||
double_class: ObjectReference,
|
pub double_class: ObjectReference,
|
||||||
float_class: ObjectReference,
|
pub float_class: ObjectReference,
|
||||||
integer_class: ObjectReference,
|
pub int_class: ObjectReference,
|
||||||
long_class: ObjectReference,
|
pub long_class: ObjectReference,
|
||||||
short_class: ObjectReference,
|
pub short_class: ObjectReference,
|
||||||
boolean_class: ObjectReference,
|
pub boolean_class: ObjectReference,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -228,6 +228,27 @@ impl ClassStore {
|
||||||
AbstractTypeKind::Boolean() => {
|
AbstractTypeKind::Boolean() => {
|
||||||
Some(self.primitive_classes.boolean_class)
|
Some(self.primitive_classes.boolean_class)
|
||||||
}
|
}
|
||||||
|
AbstractTypeKind::Byte() => {
|
||||||
|
Some(self.primitive_classes.byte_class)
|
||||||
|
}
|
||||||
|
AbstractTypeKind::Char() => {
|
||||||
|
Some(self.primitive_classes.char_class)
|
||||||
|
}
|
||||||
|
AbstractTypeKind::Double() => {
|
||||||
|
Some(self.primitive_classes.double_class)
|
||||||
|
}
|
||||||
|
AbstractTypeKind::Float() => {
|
||||||
|
Some(self.primitive_classes.float_class)
|
||||||
|
}
|
||||||
|
AbstractTypeKind::Int() => {
|
||||||
|
Some(self.primitive_classes.int_class)
|
||||||
|
}
|
||||||
|
AbstractTypeKind::Long() => {
|
||||||
|
Some(self.primitive_classes.long_class)
|
||||||
|
}
|
||||||
|
AbstractTypeKind::Short() => {
|
||||||
|
Some(self.primitive_classes.short_class)
|
||||||
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ impl <'i>ClassMethodIterator<'i> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'i> Iterator for ClassMethodIterator<'i>{
|
impl <'i> Iterator for ClassMethodIterator<'i> {
|
||||||
type Item = (usize, usize, &'i MethodInfo); // class index, method index, method info
|
type Item = (usize, usize, &'i MethodInfo); // class index, method index, method info
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
|
186
src/jvm.rs
186
src/jvm.rs
|
@ -13,8 +13,8 @@ use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info
|
||||||
use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry };
|
use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry };
|
||||||
use crate::iterators::{ ClassMethodIterator, ClassFieldIterator };
|
use crate::iterators::{ ClassMethodIterator, ClassFieldIterator };
|
||||||
use crate::native_methods;
|
use crate::native_methods;
|
||||||
use crate::native_methods::ignore_call;
|
use crate::native_methods::{ EntryPoint, ignore_call };
|
||||||
use crate::native_registry::{ NativeRegistry };
|
use crate::native_registry::NativeRegistry;
|
||||||
use crate::stackframe;
|
use crate::stackframe;
|
||||||
use crate::stackframe::{ StackFrame, StackValue, OperandStack };
|
use crate::stackframe::{ StackFrame, StackValue, OperandStack };
|
||||||
|
|
||||||
|
@ -233,10 +233,6 @@ impl JVM {
|
||||||
primitive_class_object
|
primitive_class_object
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_native(&mut self, class_name: &str, method_name: &str, method_descriptor: &MethodDescriptor) {
|
|
||||||
self.native_registry.register("java/lang/System", "registerNatives", ignore_call);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[&str]) -> Result<(), Error> {
|
pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[&str]) -> Result<(), Error> {
|
||||||
let entry_class = JavaClassFile {
|
let entry_class = JavaClassFile {
|
||||||
minor_version: 0,
|
minor_version: 0,
|
||||||
|
@ -251,12 +247,17 @@ impl JVM {
|
||||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: class_name.to_string() }),
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: class_name.to_string() }),
|
||||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: method_name.to_string() }),
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: method_name.to_string() }),
|
||||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "([Ljava/lang/String;)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::MethodRef(ConstantMethodRefInfo { class_index: 1, name_and_type_index: 11}), // 10
|
||||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 12 } ),
|
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 12, descriptor_index: 13 }),
|
||||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/String".to_string() }),
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "populateUnsafeConstants".to_string() }),
|
||||||
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 14, descriptor_index: 15 }),
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "()V".to_string() }),
|
||||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "<init>".to_string() }),
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 15 }),
|
||||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "([B)V".to_string() }), // 15
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "jdk/internal/misc/UnsafeConstants".to_string() }), // 15
|
||||||
|
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 17, name_and_type_index: 19}),
|
||||||
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 18 }),
|
||||||
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/System".to_string() }),
|
||||||
|
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 20, descriptor_index: 13 }),
|
||||||
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "initPhase1".to_string() }),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() },
|
access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() },
|
||||||
|
@ -267,7 +268,7 @@ impl JVM {
|
||||||
methods: Box::new([
|
methods: Box::new([
|
||||||
MethodInfo {
|
MethodInfo {
|
||||||
access_flags: MethodAccessFlagMask {
|
access_flags: MethodAccessFlagMask {
|
||||||
mask: MethodAccessFlag::Public.discriminant() | MethodAccessFlag::Static.discriminant()
|
mask: MethodAccessFlag::Private.discriminant() | MethodAccessFlag::Static.discriminant()
|
||||||
},
|
},
|
||||||
name: "call_main".to_string(),
|
name: "call_main".to_string(),
|
||||||
descriptor: MethodDescriptor {
|
descriptor: MethodDescriptor {
|
||||||
|
@ -287,8 +288,22 @@ impl JVM {
|
||||||
max_locals: 1,
|
max_locals: 1,
|
||||||
code: Bytecode {
|
code: Bytecode {
|
||||||
bytes: Box::new([
|
bytes: Box::new([
|
||||||
0x2A_u8.to_be(), // aload_0
|
// access something from UnsafeConstants
|
||||||
|
0x12_u8.to_be(), // ldc
|
||||||
|
0x0E_u8.to_be(), // index 14 into the constant pool
|
||||||
|
0x57_u8.to_be(), // pop
|
||||||
|
|
||||||
|
// Update UnsafeConstants to actual values
|
||||||
|
0xb8_u8.to_be(), // invokestatic
|
||||||
|
0x0A_u16.to_be_bytes()[0], // index 10 into the constant
|
||||||
|
0x0A_u16.to_be_bytes()[1], // pool
|
||||||
|
|
||||||
|
// call initPhase1
|
||||||
|
0xb8_u8.to_be(), // invokestatic
|
||||||
|
0x10_u16.to_be_bytes()[0], // index 10 into the constant
|
||||||
|
0x10_u16.to_be_bytes()[1], // pool
|
||||||
|
|
||||||
|
0x2A_u8.to_be(), // aload_0
|
||||||
0xB8_u8.to_be(), // invokestatic
|
0xB8_u8.to_be(), // invokestatic
|
||||||
0x04_u16.to_be_bytes()[0], // index 4 into the constant
|
0x04_u16.to_be_bytes()[0], // index 4 into the constant
|
||||||
0x04_u16.to_be_bytes()[1], // pool
|
0x04_u16.to_be_bytes()[1], // pool
|
||||||
|
@ -301,29 +316,47 @@ impl JVM {
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
MethodInfo {
|
||||||
|
access_flags: MethodAccessFlagMask {
|
||||||
|
mask: MethodAccessFlag::Private.discriminant() | MethodAccessFlag::Static.discriminant() | MethodAccessFlag::Native.discriminant()
|
||||||
|
},
|
||||||
|
name: "populateUnsafeConstants".to_string(),
|
||||||
|
descriptor: MethodDescriptor {
|
||||||
|
argument_types: Box::new([]),
|
||||||
|
return_type: AbstractTypeDescription {
|
||||||
|
array_level: 0,
|
||||||
|
kind: AbstractTypeKind::Void(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
code_attribute_index: 0,
|
||||||
|
attributes: Box::new([])
|
||||||
|
}
|
||||||
]),
|
]),
|
||||||
attributes: Box::new([]),
|
attributes: Box::new([]),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.class_store.add_class(entry_class, true)?; // 0
|
self.native_registry.register("::EntryPoint", "populateUnsafeConstants", EntryPoint::populate_unsafe_constants);
|
||||||
self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1
|
|
||||||
self.load_class(&"java/lang/Object".to_string())?; // 2
|
self.class_store.add_class(entry_class, true)?; // 0
|
||||||
self.load_class(&"java/lang/Number".to_string())?; // 3
|
self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1
|
||||||
self.load_class(&"java/lang/Byte".to_string())?; // 4
|
self.load_class(&"java/lang/Object".to_string())?; // 2
|
||||||
self.load_class(&"java/lang/String".to_string())?; // 5
|
self.load_class(&"java/lang/Number".to_string())?; // 3
|
||||||
self.load_class(&"java/lang/Class".to_string())?; // 6
|
let byte_class_index = self.load_class(&"java/lang/Byte".to_string())?; // 4
|
||||||
|
let string_class_index = self.load_class(&"java/lang/String".to_string())?; // 5
|
||||||
|
let class_class_index = self.load_class(&"java/lang/Class".to_string())?; // 6
|
||||||
|
let system_class_index = self.load_class(&"java/lang/System".to_string())?; // 7
|
||||||
|
|
||||||
self.make_class_class("Ljava/lang/Byte;");
|
self.make_class_class("Ljava/lang/Byte;");
|
||||||
self.make_class_class("Ljava/lang/String;");
|
self.make_class_class("Ljava/lang/String;");
|
||||||
self.make_array_class(
|
self.make_array_class(
|
||||||
self.class_store.get_class_objectref_from_index(4),
|
self.class_store.get_class_objectref_from_index(byte_class_index),
|
||||||
AbstractTypeDescription {
|
AbstractTypeDescription {
|
||||||
array_level: 0,
|
array_level: 0,
|
||||||
kind: AbstractTypeKind::Classname("java/lang/Byte".into()),
|
kind: AbstractTypeKind::Classname("java/lang/Byte".into()),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
self.make_array_class(
|
self.make_array_class(
|
||||||
self.class_store.get_class_objectref_from_index(5),
|
self.class_store.get_class_objectref_from_index(string_class_index),
|
||||||
AbstractTypeDescription {
|
AbstractTypeDescription {
|
||||||
array_level: 0,
|
array_level: 0,
|
||||||
kind: AbstractTypeKind::Classname("java/lang/String".into()),
|
kind: AbstractTypeKind::Classname("java/lang/String".into()),
|
||||||
|
@ -332,18 +365,24 @@ impl JVM {
|
||||||
|
|
||||||
self.heap_area.fill_byte_cache(&self.class_store);
|
self.heap_area.fill_byte_cache(&self.class_store);
|
||||||
|
|
||||||
let int_class_ref = self.make_primitive_class("int", "I");
|
self.class_store.primitive_classes.int_class = self.make_primitive_class("int", "I");
|
||||||
let byte_class_ref = self.make_primitive_class("byte", "B");
|
self.class_store.primitive_classes.byte_class = self.make_primitive_class("byte", "B");
|
||||||
|
self.class_store.primitive_classes.char_class = self.make_primitive_class("char", "C");
|
||||||
|
self.class_store.primitive_classes.long_class = self.make_primitive_class("long", "J");
|
||||||
|
self.class_store.primitive_classes.float_class = self.make_primitive_class("float", "F");
|
||||||
|
self.class_store.primitive_classes.short_class = self.make_primitive_class("short", "S");
|
||||||
|
self.class_store.primitive_classes.double_class = self.make_primitive_class("double", "D");
|
||||||
|
self.class_store.primitive_classes.boolean_class = self.make_primitive_class("boolean", "Z");
|
||||||
|
|
||||||
self.make_array_class(
|
self.make_array_class(
|
||||||
int_class_ref,
|
self.class_store.primitive_classes.int_class,
|
||||||
AbstractTypeDescription {
|
AbstractTypeDescription {
|
||||||
array_level: 0,
|
array_level: 0,
|
||||||
kind: AbstractTypeKind::Int(),
|
kind: AbstractTypeKind::Int(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
self.make_array_class(
|
self.make_array_class(
|
||||||
byte_class_ref,
|
self.class_store.primitive_classes.byte_class,
|
||||||
AbstractTypeDescription {
|
AbstractTypeDescription {
|
||||||
array_level: 0,
|
array_level: 0,
|
||||||
kind: AbstractTypeKind::Byte(),
|
kind: AbstractTypeKind::Byte(),
|
||||||
|
@ -366,7 +405,6 @@ impl JVM {
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<(), Error> {
|
pub fn run(&mut self) -> Result<(), Error> {
|
||||||
while self.stack_frames.len() != 0 {
|
while self.stack_frames.len() != 0 {
|
||||||
println!("Enter bytecode loop:");
|
|
||||||
|
|
||||||
let jvm_op = self.bytecode_loop()?;
|
let jvm_op = self.bytecode_loop()?;
|
||||||
match jvm_op {
|
match jvm_op {
|
||||||
|
@ -517,6 +555,11 @@ impl JVM {
|
||||||
|
|
||||||
FieldValue::Long(long_entry.value)
|
FieldValue::Long(long_entry.value)
|
||||||
},
|
},
|
||||||
|
AbstractTypeKind::Float() => {
|
||||||
|
let float_entry = class_file.pool_float_entry(constant_value_info.constant_value_index)?;
|
||||||
|
|
||||||
|
FieldValue::Float(float_entry.value)
|
||||||
|
},
|
||||||
AbstractTypeKind::Classname(ref name) => {
|
AbstractTypeKind::Classname(ref name) => {
|
||||||
if name == "java/lang/String" {
|
if name == "java/lang/String" {
|
||||||
let string_entry = class_file.gather_string(constant_value_info.constant_value_index)?;
|
let string_entry = class_file.gather_string(constant_value_info.constant_value_index)?;
|
||||||
|
@ -585,15 +628,15 @@ impl JVM {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytecode_loop(&mut self) -> Result<JVMCallbackOperation, Error> {
|
fn bytecode_loop(&mut self) -> Result<JVMCallbackOperation, Error> {
|
||||||
|
//println!("Enter bytecode loop:");
|
||||||
|
|
||||||
let frame = {
|
let frame_index = self.stack_frames.len() - 1;
|
||||||
let frame_index = self.stack_frames.len() - 1;
|
let frame = &mut self.stack_frames[frame_index];
|
||||||
&mut self.stack_frames[frame_index]
|
|
||||||
};
|
|
||||||
let class = self.class_store.class_file_from_idx(frame.class_index).unwrap();
|
let class = self.class_store.class_file_from_idx(frame.class_index).unwrap();
|
||||||
let method = & class.methods[frame.method_index as usize];
|
let method = & class.methods[frame.method_index as usize];
|
||||||
|
|
||||||
if method.access_flags & MethodAccessFlag::Native {
|
if method.access_flags & MethodAccessFlag::Native {
|
||||||
|
println!("{:25}.{:15}: (native)", class.get_classname().unwrap(), method.name);
|
||||||
return self.native_call()
|
return self.native_call()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,10 +647,17 @@ impl JVM {
|
||||||
let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize);
|
let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize);
|
||||||
frame.instruction_pointer += offset as u32;
|
frame.instruction_pointer += offset as u32;
|
||||||
|
|
||||||
println!("{:25}.{:15}:{:<10}{instruction:?}", class.get_classname().unwrap(), method.name, frame.instruction_pointer);
|
println!("{}{:25}.{:15}:{:<10}{instruction:?}", " ".repeat(frame_index), class.get_classname().unwrap(), method.name, frame.instruction_pointer);
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
|
|
||||||
|
Instruction::AddInt() => {
|
||||||
|
let value_0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||||
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||||
|
|
||||||
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_0 + value_1)))?;
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::ArrayLength() => {
|
Instruction::ArrayLength() => {
|
||||||
let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||||
|
|
||||||
|
@ -659,6 +709,24 @@ impl JVM {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Instruction::BranchNonNegative(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::BranchNonZero(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::BranchNull(branch_offset) => {
|
Instruction::BranchNull(branch_offset) => {
|
||||||
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||||
|
|
||||||
|
@ -840,9 +908,6 @@ impl JVM {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if callee_method_info.access_flags & MethodAccessFlag::Native {
|
|
||||||
}
|
|
||||||
|
|
||||||
let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?;
|
let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?;
|
||||||
// TODO: Throw exception on fail
|
// TODO: Throw exception on fail
|
||||||
|
|
||||||
|
@ -982,6 +1047,12 @@ impl JVM {
|
||||||
|
|
||||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(string_obj_ref)))?;
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(string_obj_ref)))?;
|
||||||
},
|
},
|
||||||
|
ConstantPoolInfo::Float(_) => {
|
||||||
|
// TODO: Handle error instead of unwrap
|
||||||
|
let float_constant = class.gather_float(index as u16).unwrap();
|
||||||
|
|
||||||
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(float_constant)))?;
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
println!("{:?}", class.pool_entry(index as u16).unwrap());
|
println!("{:?}", class.pool_entry(index as u16).unwrap());
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -1000,11 +1071,7 @@ impl JVM {
|
||||||
let array_level = class_name.len() - component_name.len();
|
let array_level = class_name.len() - component_name.len();
|
||||||
let array_type_desc = AbstractTypeDescription {
|
let array_type_desc = AbstractTypeDescription {
|
||||||
array_level: array_level as u8,
|
array_level: array_level as u8,
|
||||||
kind: if component_name.len() == 1 {
|
kind: component_name.into(),
|
||||||
component_name.into()
|
|
||||||
} else {
|
|
||||||
AbstractTypeKind::Classname(component_name.to_string())
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(array_ref) = self.class_store.get_array_class_ref(&array_type_desc) {
|
if let Some(array_ref) = self.class_store.get_array_class_ref(&array_type_desc) {
|
||||||
|
@ -1166,6 +1233,10 @@ impl JVM {
|
||||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(new_object)))?;
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(new_object)))?;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Instruction::Pop() => {
|
||||||
|
wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::PushConstInt0() => {
|
Instruction::PushConstInt0() => {
|
||||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(0)))?;
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(0)))?;
|
||||||
}
|
}
|
||||||
|
@ -1189,6 +1260,26 @@ impl JVM {
|
||||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(ObjectReference::NULL)))?;
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(ObjectReference::NULL)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Instruction::PutField(fieldref_index) => {
|
||||||
|
let (_target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?;
|
||||||
|
|
||||||
|
let value = match expected_field_descriptor.as_str() {
|
||||||
|
"J" | "D" => wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_2(0))?,
|
||||||
|
_ => match wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))? {
|
||||||
|
StackValue::Int(i) => FieldValue::Int(i),
|
||||||
|
StackValue::Reference(r) => FieldValue::Reference(r),
|
||||||
|
stack_value @ _ => {
|
||||||
|
println!("{stack_value:?}");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||||
|
|
||||||
|
self.heap_area.object_area.set_object_field(this_object, target_field_name, value, frame.class_index, &self.class_store)?;
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::PutStatic(fieldref_index) => {
|
Instruction::PutStatic(fieldref_index) => {
|
||||||
let (target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?;
|
let (target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?;
|
||||||
|
|
||||||
|
@ -1228,7 +1319,13 @@ impl JVM {
|
||||||
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||||
|
|
||||||
FieldValue::Boolean((int_value & 1) != 0)
|
FieldValue::Boolean((int_value & 1) != 0)
|
||||||
},
|
}
|
||||||
|
|
||||||
|
(0, AbstractTypeKind::Int()) => {
|
||||||
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||||
|
|
||||||
|
FieldValue::Int(int_value)
|
||||||
|
}
|
||||||
|
|
||||||
(0..=255, AbstractTypeKind::Classname(_field_type_name)) => {
|
(0..=255, AbstractTypeKind::Classname(_field_type_name)) => {
|
||||||
let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||||
|
@ -1404,7 +1501,8 @@ fn load_local_int(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackF
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut VecDeque<StackValue>, argument_types: &Box<[AbstractTypeDescription]>, stack: &mut OperandStack) -> Result<(), Error> {
|
fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut VecDeque<StackValue>, argument_types: &Box<[AbstractTypeDescription]>, stack: &mut OperandStack) -> Result<(), Error> {
|
||||||
for argument_type in argument_types {
|
for argument_type_index in 0..argument_types.len() {
|
||||||
|
let argument_type = &argument_types[argument_types.len() - argument_type_index - 1];
|
||||||
if argument_type.array_level != 0 {
|
if argument_type.array_level != 0 {
|
||||||
// TODO: Type checking
|
// TODO: Type checking
|
||||||
arguments.push_front(
|
arguments.push_front(
|
||||||
|
@ -1540,7 +1638,7 @@ fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut Ve
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_stackframe_error<T>(class: &JavaClassFile, method: &MethodInfo, frame_result: Result<T, stackframe::Error>) -> Result<T, Error> {
|
pub fn wrap_stackframe_error<T>(class: &JavaClassFile, method: &MethodInfo, frame_result: Result<T, stackframe::Error>) -> Result<T, Error> {
|
||||||
match frame_result {
|
match frame_result {
|
||||||
Ok(t) => Ok(t),
|
Ok(t) => Ok(t),
|
||||||
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
use crate::stackframe::StackFrame;
|
||||||
use crate::jvm::Error;
|
use crate::jvm::Error;
|
||||||
use crate::jvm::JVMCallbackOperation;
|
use crate::jvm::JVMCallbackOperation;
|
||||||
use crate::jvm::JVM;
|
use crate::jvm::JVM;
|
||||||
|
|
|
@ -74,6 +74,32 @@ impl OperandStack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pop_computational_2(&mut self, index: usize) -> Result<FieldValue, Error> {
|
||||||
|
let absolute_index = self.depth as usize - 1 - index;
|
||||||
|
let value_top = self.stack[absolute_index];
|
||||||
|
let value_bot = self.stack[absolute_index - 1];
|
||||||
|
|
||||||
|
self.depth -= 2;
|
||||||
|
|
||||||
|
match (value_bot, value_top) {
|
||||||
|
(StackValue::Long0(l0), StackValue::Long1(l1)) => {
|
||||||
|
let l0_bytes = l0.to_ne_bytes();
|
||||||
|
let l1_bytes = l1.to_ne_bytes();
|
||||||
|
let concat_bytes = [l0_bytes[0], l0_bytes[1], l0_bytes[2], l0_bytes[3], l1_bytes[0], l1_bytes[1], l1_bytes[2], l1_bytes[3]];
|
||||||
|
|
||||||
|
Ok(FieldValue::Long(i64::from_ne_bytes(concat_bytes)))
|
||||||
|
}
|
||||||
|
(StackValue::Double0(d0), StackValue::Double1(d1)) => {
|
||||||
|
let d0_bytes = d0.to_ne_bytes();
|
||||||
|
let d1_bytes = d1.to_ne_bytes();
|
||||||
|
let concat_bytes = [d0_bytes[0], d0_bytes[1], d0_bytes[2], d0_bytes[3], d1_bytes[0], d1_bytes[1], d1_bytes[2], d1_bytes[3]];
|
||||||
|
|
||||||
|
Ok(FieldValue::Double(f64::from_ne_bytes(concat_bytes)))
|
||||||
|
}
|
||||||
|
_ => Err(Error::LocalError(format!("Mismatched type at index {index} of the function operand stack, expected type with computational type 2 but found '{value_bot:?}, {value_top:?}'")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pop_computational_1(&mut self, index: usize) -> Result<StackValue, Error> {
|
pub fn pop_computational_1(&mut self, index: usize) -> Result<StackValue, Error> {
|
||||||
let absolute_index = self.depth as usize - 1 - index;
|
let absolute_index = self.depth as usize - 1 - index;
|
||||||
|
|
Loading…
Reference in a new issue