Entrypoint call entry methodgit add .
This commit is contained in:
parent
ea3666aad3
commit
2042007242
9 changed files with 573 additions and 119 deletions
210
src/jvm.rs
210
src/jvm.rs
|
@ -1,14 +1,28 @@
|
|||
use core::fmt::{Display, Formatter};
|
||||
|
||||
use std::error::Error as ErrorTrait;
|
||||
|
||||
use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag};
|
||||
use crate::bytecode::{ Bytecode, Instruction };
|
||||
use crate::classfile;
|
||||
use crate::classfile::{ JavaClassFile, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData };
|
||||
use crate::classstore;
|
||||
use crate::classstore::ClassStore;
|
||||
use crate::stackframe::StackFrame;
|
||||
use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo};
|
||||
use crate::stackframe::{ StackFrame, Value };
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
ClassStoreError(classstore::Error),
|
||||
ClassFileError(classfile::Error),
|
||||
BadNameError(String),
|
||||
RunTimeError(String),
|
||||
}
|
||||
|
||||
impl From<classfile::Error> for Error {
|
||||
fn from(value: classfile::Error) -> Self {
|
||||
return Error::ClassFileError(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<classstore::Error> for Error {
|
||||
|
@ -42,21 +56,203 @@ impl JVM {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_class(&mut self, name: &String) -> Result<String, classstore::Error> {
|
||||
return self.class_store.load_class(name);
|
||||
pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[Value]) -> Result<(), Error> {
|
||||
let entry_class = JavaClassFile {
|
||||
minor_version: 0,
|
||||
major_version: 0,
|
||||
constant_pool: Box::new([
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "::EntryPoint".to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "Code".to_string() }),
|
||||
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 5, name_and_type_index: 6}),
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 7 }),
|
||||
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() }),
|
||||
]
|
||||
),
|
||||
access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() },
|
||||
this_class: 1,
|
||||
super_class: 0,
|
||||
interfaces: Box::new([]),
|
||||
fields: Box::new([]),
|
||||
methods: Box::new([
|
||||
MethodInfo {
|
||||
access_flags: MethodAccessFlagMask {
|
||||
mask: MethodAccessFlag::Public.discriminant() | MethodAccessFlag::Static.discriminant()
|
||||
},
|
||||
name: "call_main".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: 0,
|
||||
max_locals: 0,
|
||||
code: Bytecode {
|
||||
bytes: Box::new([
|
||||
0xB8_u8.to_be(), // invokestatic
|
||||
0x04_u16.to_be_bytes()[0], // index 4 into the constant
|
||||
0x04_u16.to_be_bytes()[1], // pool
|
||||
]),
|
||||
},
|
||||
exception_table: Box::new([]),
|
||||
attributes: Box::new([]),
|
||||
}
|
||||
)
|
||||
}
|
||||
])
|
||||
}
|
||||
]),
|
||||
attributes: Box::new([]),
|
||||
};
|
||||
|
||||
self.stack_frames.push(
|
||||
StackFrame::new(&entry_class, 0, 0, arguments),
|
||||
);
|
||||
|
||||
self.class_store.add_class(entry_class, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn invoke_static(&mut self, class_name: &String, method_name: &String) -> Result<(), Error> {
|
||||
pub fn run(&mut self) -> Result<(), Error> {
|
||||
while self.stack_frames.len() != 0 {
|
||||
let jvm_op = self.bytecode_loop()?;
|
||||
match jvm_op {
|
||||
JVMCallbackOperation::PopFrame() => self.stack_frames.truncate(self.stack_frames.len() - 1),
|
||||
JVMCallbackOperation::PushFrame(frame) => self.stack_frames.push(frame),
|
||||
JVMCallbackOperation::LoadClass(name) => {
|
||||
self.class_store.load_class(&name)?;
|
||||
()
|
||||
},
|
||||
JVMCallbackOperation::InitClass(name) => {
|
||||
self.init_class(*self.class_store.class_idx_from_name(&name).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (class_file, class_index) = self.class_store.get_or_load_class(class_name)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_class(&mut self, class_idx: usize) {
|
||||
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
||||
let clinit_idx = class_file.find_method_index(&"<clinit>".to_string());
|
||||
|
||||
// TODO: ConstantValue Attributes (final)
|
||||
// TODO: Static Stuff
|
||||
|
||||
}
|
||||
|
||||
fn prepare_invoke_static(&mut self, class_index: usize, method_name: &String, arguments: &[Value]) -> Result<(), Error> {
|
||||
|
||||
let class_file = self.class_store.class_file_from_idx(class_index).unwrap();
|
||||
|
||||
let method_index = class_file.find_method_index(method_name)
|
||||
.ok_or(Error::BadNameError(format!("Could not find method '{}' in class '{}'", method_name, class_name)))?;
|
||||
.ok_or(Error::BadNameError(format!("Could not find method '{}' in class '{}'", method_name, class_file.get_classname()?)))?;
|
||||
|
||||
let new_frame = StackFrame::new(class_file, class_index, method_index.try_into().expect(&format!("Bad method index: {}", method_index)));
|
||||
let new_frame = StackFrame::new(
|
||||
class_file,
|
||||
class_index,
|
||||
method_index.try_into().expect(&format!("Bad method index: {}", method_index)),
|
||||
arguments
|
||||
);
|
||||
|
||||
self.stack_frames.push(new_frame);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn bytecode_loop(&mut self) -> Result<JVMCallbackOperation, Error> {
|
||||
|
||||
let frame = {
|
||||
let frame_index = self.stack_frames.len() - 1;
|
||||
&mut self.stack_frames[frame_index]
|
||||
};
|
||||
let class = self.class_store.class_file_from_idx(frame.class_index).unwrap();
|
||||
let method = & class.methods[frame.method_index as usize];
|
||||
let code_attr = method.get_code_attribute().unwrap();
|
||||
let bytecode = & code_attr.code;
|
||||
|
||||
while frame.instruction_pointer as usize != bytecode.bytes.len() {
|
||||
let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize);
|
||||
frame.instruction_pointer += offset as u32;
|
||||
|
||||
match instruction {
|
||||
Instruction::InvokeStatic(methodref_index) => {
|
||||
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?;
|
||||
|
||||
if ! self.class_store.have_class(supplied_class_name) {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::LoadClass(supplied_class_name.to_string()));
|
||||
}
|
||||
if ! self.class_store.was_init(supplied_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(supplied_class_name.to_string()));
|
||||
}
|
||||
|
||||
let (callee_class_file, callee_class_index) = self.class_store.get_class(supplied_class_name)?;
|
||||
// TODO: Throw exception on fail
|
||||
let callee_method_index = callee_class_file.find_method_index(supplied_method_name).unwrap();
|
||||
// TODO: Throw exception on fail
|
||||
let callee_method_info = &callee_class_file.methods[callee_method_index];
|
||||
|
||||
let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?;
|
||||
// TODO: Throw exception on fail
|
||||
|
||||
if supplied_descriptor != callee_method_info.descriptor {
|
||||
// TODO: Throw exception on fail
|
||||
return Err(Error::RunTimeError(format!(
|
||||
"Mismatched method descriptors between caller and callee: Caller ({}) wanted '{}' but found '{}' on Callee ({})",
|
||||
class.get_classname().unwrap(),
|
||||
supplied_descriptor_string,
|
||||
callee_method_info.descriptor.source_string(),
|
||||
supplied_class_name,
|
||||
)));
|
||||
}
|
||||
|
||||
let arguments = Vec::new();
|
||||
|
||||
let new_frame = StackFrame::new(
|
||||
callee_class_file,
|
||||
callee_class_index,
|
||||
callee_method_index as u16,
|
||||
&arguments.into_boxed_slice(),
|
||||
);
|
||||
|
||||
//println!("{} {} {}", class_name, method_name, method_descriptor);
|
||||
|
||||
return Ok(JVMCallbackOperation::PushFrame(new_frame));
|
||||
},
|
||||
|
||||
_ => {
|
||||
return Err(Error::RunTimeError(format!("Opcode not implemented yet: {:?}", instruction)))
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Ok(JVMCallbackOperation::PopFrame())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum JVMCallbackOperation {
|
||||
PopFrame(),
|
||||
PushFrame(StackFrame),
|
||||
LoadClass(String),
|
||||
InitClass(String),
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue