checkpoint

This commit is contained in:
james 2025-11-12 01:14:47 +10:30
parent 6c5900e79a
commit e39ae877a3
No known key found for this signature in database
GPG Key ID: E1FFBA228F4CAD87
15 changed files with 2457 additions and 87 deletions

3
.gitignore vendored
View File

@ -21,3 +21,6 @@ Cargo.lock
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
data
lib
output

View File

@ -4,9 +4,15 @@ version = "0.1.0"
edition = "2024"
[dependencies]
spacetimedb-sdk = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
#spacetimedb-core = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
#spacetimedb-lib = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
#spacetimedb-sats = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
#spacetimedb-client-api-messages = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
#bindings = { git = "https://github.com/infernap12/bitcraft-bindings", branch = "class_de" }
deku = { version = "0.20.0", features = ["logging"] }
deku_derive = "0.20.0"
log = "0.4.28"
env_logger = "0.11.8"
itertools = "0.14.0"
sevenz-rust2 = { version = "0.19.3", features = ["brotli", "zstd"] }
[build-dependencies]
bindgen = "0.72.1"
[lints.rust]
missing_docs = "warn"

18
build.rs Normal file
View File

@ -0,0 +1,18 @@
use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=classfile_constants.h");
let bindings = bindgen::Builder::default()
.header("data/java/base/jmod/include/classfile_constants.h")
.rustified_enum(".*")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}

624
src/attributes.rs Normal file
View File

@ -0,0 +1,624 @@
use std::fmt::Display;
use deku_derive::DekuRead;
use deku::DekuContainerRead;
use log::trace;
use crate::class_file::{ClassFile, CpInfo};
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct AttributeInfo {
pub attribute_name_index: u16,
pub attribute_length: u32,
#[deku(count = "attribute_length")]
pub info: Vec<u8>,
#[deku(skip)]
pub interpreted: Option<Attribute>,
}
#[derive(Clone, PartialEq, Debug)]
pub enum Attribute {
// "Critical"
ConstantValue,
Code(CodeAttribute),
StackMapTable(Vec<u8>),
BootstrapMethods,
NestHost,
NestMembers,
PermittedSubclasses,
SourceFile(u16),
LineNumberTable(LineNumberTableAttribute),
Exceptions(Vec<u8>),
InnerClasses(Vec<u8>),
Signature(u16),
LocalVariableTable(LocalVariableTableAttribute),
Unknown(String, Vec<u8>),
}
//noinspection SpellCheckingInspection
#[allow(non_camel_case_types)]
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(id_type = "u8", ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub enum ArrayType {
#[deku(id = "4")]
T_BOOLEAN,
#[deku(id = "5")]
T_CHAR,
#[deku(id = "6")]
T_FLOAT,
#[deku(id = "7")]
T_DOUBLE,
#[deku(id = "8")]
T_BYTE,
#[deku(id = "9")]
T_SHORT,
#[deku(id = "10")]
T_INT,
#[deku(id = "11")]
T_LONG,
}
//noinspection SpellCheckingInspection
#[allow(non_camel_case_types)]
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(id_type = "u8", ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub enum Ops {
// Constants
#[deku(id = 0x00)]
nop,
#[deku(id = 0x01)]
aconst_null,
#[deku(id = 0x02)]
iconst_m1,
#[deku(id = 0x03)]
iconst_0,
#[deku(id = 0x04)]
iconst_1,
#[deku(id = 0x05)]
iconst_2,
#[deku(id = 0x06)]
iconst_3,
#[deku(id = 0x07)]
iconst_4,
#[deku(id = 0x08)]
iconst_5,
#[deku(id = 0x09)]
lconst_0,
#[deku(id = 0x0a)]
lconst_1,
#[deku(id = 0x0b)]
fconst_0,
#[deku(id = 0x0c)]
fconst_1,
#[deku(id = 0x0d)]
fconst_2,
#[deku(id = 0x0e)]
dconst_0,
#[deku(id = 0x0f)]
dconst_1,
#[deku(id = 0x10)]
bipush(u8),
#[deku(id = 0x11)]
sipush(u16),
#[deku(id = 0x12)]
ldc(u8),
#[deku(id = 0x13)]
ldc_w(u16),
#[deku(id = 0x14)]
ldc2_w(u16),
// Loads
#[deku(id = 0x15)]
iload(u8),
#[deku(id = 0x16)]
lload(u8),
#[deku(id = 0x17)]
fload(u8),
#[deku(id = 0x18)]
dload(u8),
#[deku(id = 0x19)]
aload(u8),
#[deku(id = 0x1A)]
iload_0,
#[deku(id = 0x1B)]
iload_1,
#[deku(id = 0x1C)]
iload_2,
#[deku(id = 0x1D)]
iload_3,
#[deku(id = 0x1E)]
lload_0,
#[deku(id = 0x1F)]
lload_1,
#[deku(id = 0x20)]
lload_2,
#[deku(id = 0x21)]
lload_3,
#[deku(id = 0x22)]
fload_0,
#[deku(id = 0x23)]
fload_1,
#[deku(id = 0x24)]
fload_2,
#[deku(id = 0x25)]
fload_3,
#[deku(id = 0x26)]
dload_0,
#[deku(id = 0x27)]
dload_1,
#[deku(id = 0x28)]
dload_2,
#[deku(id = 0x29)]
dload_3,
#[deku(id = 0x2A)]
aload_0,
#[deku(id = 0x2B)]
aload_1,
#[deku(id = 0x2C)]
aload_2,
#[deku(id = 0x2D)]
aload_3,
#[deku(id = 0x2E)]
iaload,
#[deku(id = 0x2F)]
laload,
#[deku(id = 0x30)]
faload,
#[deku(id = 0x31)]
daload,
#[deku(id = 0x32)]
aaload,
#[deku(id = 0x33)]
baload,
#[deku(id = 0x34)]
caload,
#[deku(id = 0x35)]
saload,
// Stores
#[deku(id = 0x36)]
istore(u8),
#[deku(id = 0x37)]
lstore(u8),
#[deku(id = 0x38)]
fstore(u8),
#[deku(id = 0x39)]
dstore(u8),
#[deku(id = 0x3A)]
astore(u8),
#[deku(id = 0x3B)]
istore_0,
#[deku(id = 0x3C)]
istore_1,
#[deku(id = 0x3D)]
istore_2,
#[deku(id = 0x3E)]
istore_3,
#[deku(id = 0x3F)]
lstore_0,
#[deku(id = 0x40)]
lstore_1,
#[deku(id = 0x41)]
lstore_2,
#[deku(id = 0x42)]
lstore_3,
#[deku(id = 0x43)]
fstore_0,
#[deku(id = 0x44)]
fstore_1,
#[deku(id = 0x45)]
fstore_2,
#[deku(id = 0x46)]
fstore_3,
#[deku(id = 0x47)]
dstore_0,
#[deku(id = 0x48)]
dstore_1,
#[deku(id = 0x49)]
dstore_2,
#[deku(id = 0x4A)]
dstore_3,
#[deku(id = 0x4B)]
astore_0,
#[deku(id = 0x4C)]
astore_1,
#[deku(id = 0x4D)]
astore_2,
#[deku(id = 0x4E)]
astore_3,
#[deku(id = 0x4F)]
iastore,
#[deku(id = 0x50)]
lastore,
#[deku(id = 0x51)]
fastore,
#[deku(id = 0x52)]
dastore,
#[deku(id = 0x53)]
aastore,
#[deku(id = 0x54)]
bastore,
#[deku(id = 0x55)]
castore,
#[deku(id = 0x56)]
sastore,
//stack
//math
#[deku(id = 0x60)]
iadd,
#[deku(id = 0x61)]
ladd,
#[deku(id = 0x62)]
fadd,
#[deku(id = 0x63)]
dadd,
#[deku(id = 0x64)]
isub,
#[deku(id = 0x65)]
lsub,
#[deku(id = 0x66)]
fsub,
#[deku(id = 0x67)]
dsub,
#[deku(id = 0x68)]
imul,
#[deku(id = 0x69)]
lmul,
#[deku(id = 0x6a)]
fmul,
#[deku(id = 0x6b)]
dmul,
#[deku(id = 0x6c)]
idiv,
#[deku(id = 0x6d)]
ldiv,
#[deku(id = 0x6e)]
fdiv,
#[deku(id = 0x6f)]
ddiv,
#[deku(id = 0x70)]
irem,
#[deku(id = 0x71)]
lrem,
#[deku(id = 0x72)]
frem,
#[deku(id = 0x73)]
drem,
#[deku(id = 0x74)]
ineg,
#[deku(id = 0x75)]
lneg,
#[deku(id = 0x76)]
fneg,
#[deku(id = 0x77)]
dneg,
#[deku(id = 0x78)]
ishl,
#[deku(id = 0x79)]
lshl,
#[deku(id = 0x7a)]
ishr,
#[deku(id = 0x7b)]
lshr,
#[deku(id = 0x7c)]
iushr,
#[deku(id = 0x7d)]
lushr,
#[deku(id = 0x7e)]
iand,
#[deku(id = 0x7f)]
land,
#[deku(id = 0x80)]
ior,
#[deku(id = 0x81)]
lor,
#[deku(id = 0x82)]
ixor,
#[deku(id = 0x83)]
lxor,
#[deku(id = 0x84)]
iinc(u8, i8),
//conversions
#[deku(id = 0x85)]
i2l,
#[deku(id = 0x86)]
i2f,
#[deku(id = 0x87)]
i2d,
#[deku(id = 0x88)]
l2i,
#[deku(id = 0x89)]
l2f,
#[deku(id = 0x8a)]
l2d,
#[deku(id = 0x8b)]
f2i,
#[deku(id = 0x8c)]
f2l,
#[deku(id = 0x8d)]
f2d,
#[deku(id = 0x8e)]
d2i,
#[deku(id = 0x8f)]
d2l,
#[deku(id = 0x90)]
d2f,
#[deku(id = 0x91)]
i2b,
#[deku(id = 0x92)]
i2c,
#[deku(id = 0x93)]
i2s,
// comparisons
// control
#[deku(id = 0xa7)]
goto(u16),
#[deku(id = 0xa8)]
jsr(u16),
#[deku(id = 0xa9)]
ret(u8),
#[deku(id = 0xaa)]
tableswitch,
#[deku(id = 0xab)]
lookupswitch,
#[deku(id = 0xac)]
ireturn,
#[deku(id = 0xad)]
lreturn,
#[deku(id = 0xae)]
freturn,
#[deku(id = 0xaf)]
dreturn,
#[deku(id = 0xb0)]
areturn,
// return
#[deku(id = 0xb1)]
return_void,
// references
#[deku(id = 0xB2)]
getstatic(u16),
#[deku(id = 0xB3)]
putstatic(u16),
#[deku(id = 0xB4)]
getfield(u16),
#[deku(id = 0xB5)]
putfield(u16),
#[deku(id = 0xB6)]
invokevirtual(u16),
#[deku(id = 0xB7)]
invokespecial(u16),
#[deku(id = 0xB8)]
invokestatic(u16),
// 4th byte always zero/0
#[deku(id = 0xB9)]
invokeinterface(u16, u8, u8),
// 3rd, 4th must be zero/0
#[deku(id = 0xBA)]
invokedynamic(u16, u16),
#[deku(id = 0xBB)]
new(u16),
#[deku(id = 0xBC)]
newarray(ArrayType),
#[deku(id = 0xBD)]
anewarray(u16),
#[deku(id = 0xBE)]
arraylength,
#[deku(id = 0xBF)]
athrow,
#[deku(id = 0xC0)]
checkcast(u16),
#[deku(id = 0xC1)]
instanceof(u16),
#[deku(id = 0xC2)]
monitorenter,
#[deku(id = 0xC3)]
monitorexit,
}
// impl TryFrom<u8> for Ops {
// type Error = ();
//
// fn try_from(value: u8) -> Result<Self, Self::Error> {
// match value {
// 0x2A => Ok(Ops::Aload0),
// 0x2B => Ok(Ops::InvokeSpecial(0)),
// 0x10 => Ok(Ops::Bipush(0)),
// 0xB5 => Ok(Ops::Putfield(0)),
// 0x14 => Ok(Ops::Ldc2_w(0)),
// 0xB1 => Ok(Ops::Retern),
// _ => Err(())
// }
// }
// }
impl AttributeInfo {
pub fn parse_attribute(&self, constant_pool: &[CpInfo]) -> Option<Attribute> {
let name = crate::class_file::pool_get_string(constant_pool, self.attribute_name_index)?;
trace!("Parsing attribute with name: {}", name);
match name.as_ref() {
"Code" => {
let (_, mut code_attr) = CodeAttribute::from_bytes((&self.info.as_slice(), 0)).ok()?;
// Recursively interpret nested attributes
for attr in &mut code_attr.attributes {
attr.interpreted = attr.parse_attribute(constant_pool);
}
Some(Attribute::Code(code_attr))
}
"SourceFile" => {
if self.info.len() >= 2 {
let source_file_index = u16::from_be_bytes([self.info[0], self.info[1]]);
Some(Attribute::SourceFile(source_file_index))
} else {
None
}
}
"LineNumberTable" => {
let (_, lnt) = LineNumberTableAttribute::from_bytes((&self.info.as_slice(), 0)).ok()?;
Some(Attribute::LineNumberTable(lnt))
}
"StackMapTable" => {
Some(Attribute::StackMapTable(self.info.clone()))
}
"Exceptions" => {
Some(Attribute::Exceptions(self.info.clone()))
}
"InnerClasses" => {
Some(Attribute::InnerClasses(self.info.clone()))
}
"Signature" => {
if self.info.len() >= 2 {
let signature_index = u16::from_be_bytes([self.info[0], self.info[1]]);
Some(Attribute::Signature(signature_index))
} else {
None
}
}
"LocalVariableTable" => {
let (_, lvt) = LocalVariableTableAttribute::from_bytes((&self.info.as_slice(), 0)).ok()?;
Some(Attribute::LocalVariableTable(lvt))
}
_ => Some(Attribute::Unknown(name.to_string(), self.info.clone())),
}
}
/// Get the interpreted attribute, parsing if necessary
pub fn get(&self, class_file: &ClassFile) -> Option<Attribute> {
if let Some(ref attr) = self.interpreted {
Some(attr.clone())
} else {
self.parse_attribute(class_file.constant_pool.as_ref())
}
}
}
impl LocalVariableTableAttribute {
}
impl Display for AttributeInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "AttributeInfo {{ name_index: {}, length: {} }}",
self.attribute_name_index,
self.attribute_length
)
}
}
impl Display for Attribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Attribute::Code(code) => write!(f, "Code attribute: {}", code),
Attribute::SourceFile(index) => write!(f, "SourceFile attribute, index: {}", index),
Attribute::LineNumberTable(data) => write!(f, "LineNumberTable attribute: {}", data),
Attribute::StackMapTable(data) => write!(f, "StackMapTable attribute, {} bytes", data.len()),
Attribute::Exceptions(data) => write!(f, "Exceptions attribute, {} bytes", data.len()),
Attribute::InnerClasses(data) => write!(f, "InnerClasses attribute, {} bytes", data.len()),
Attribute::Signature(index) => write!(f, "Signature attribute, index: {}", index),
Attribute::LocalVariableTable(table) => write!(f, "LocalVariableTable attribute: {}", table),
Attribute::Unknown(name, data) => write!(f, "Unknown attribute '{}', {} bytes", name, data.len()),
_ => { unreachable!() }
}
}
}
impl Display for CodeAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "stack={}, locals={}, code={} bytes, exceptions={}, attributes={}",
self.max_stack,
self.max_locals,
self.code_length,
self.exception_table_length,
self.attributes_count
)
}
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(endian = "big")]
pub struct CodeAttribute {
pub max_stack: u16,
pub max_locals: u16,
pub code_length: u32,
#[deku(count = "code_length")]
pub code: Vec<u8>,
pub exception_table_length: u16,
#[deku(count = "exception_table_length")]
pub exception_table: Vec<ExceptionTableEntry>,
pub attributes_count: u16,
#[deku(count = "attributes_count")]
pub attributes: Vec<AttributeInfo>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ExceptionTableEntry {
pub start_pc: u16,
pub end_pc: u16,
pub handler_pc: u16,
pub catch_type: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(endian = "big")]
pub struct LocalVariableTableAttribute {
pub local_variable_table_length: u16,
#[deku(count = "local_variable_table_length")]
pub local_variable_table: Vec<LocalVariableTableEntry>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct LocalVariableTableEntry {
pub start_pc: u16,
pub length: u16,
pub name_index: u16,
pub descriptor_index: u16,
pub index: u16,
}
impl Display for LocalVariableTableAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "local_variable_table_length={}, entries={}",
self.local_variable_table_length,
self.local_variable_table.len()
)
}
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(endian = "big")]
pub struct LineNumberTableAttribute {
pub line_number_table_length: u16,
#[deku(count = "line_number_table_length")]
pub line_number_table: Vec<LineNumberTableEntry>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct LineNumberTableEntry {
pub start_pc: u16,
pub line_number: u16,
}
impl Display for LineNumberTableAttribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "table_length={}, entries={}",
self.line_number_table_length,
self.line_number_table.len()
)
}
}

58
src/bimage.rs Normal file
View File

@ -0,0 +1,58 @@
use sevenz_rust2::ArchiveReader;
use std::collections::HashSet;
use std::fs::File;
const DEFAULT_LOCATION: &str = "./lib/modules";
pub struct Bimage {
image: ArchiveReader<File>,
modules: Vec<String>,
}
impl Default for Bimage {
fn default() -> Self {
let reader = ArchiveReader::open(DEFAULT_LOCATION, Default::default()).expect("No image location given, and unable to open/locate default image");
let mut modules = reader.archive().files.iter().filter(|e|{
e.is_directory &&
e.name.split("/").count() == 1
}).map(|e| { e.name.clone() }).collect::<HashSet<_>>().into_iter().collect::<Vec<_>>();
modules.sort();
Self {
image: reader,
modules,
}
}
}
impl Bimage {
pub fn new(path: impl AsRef<std::path::Path>) -> Self {
let reader = ArchiveReader::open(path, Default::default()).expect("Unable to find specified bimage.");
Self {
image: reader,
..Default::default()
}
}
fn resolve_path(module: &str, class: &str) -> String {
let module = if module.is_empty() { "java.base" } else { module };
let class = Self::d2s(class);
format!("{module}/classes/{class}.class")
}
fn d2s( dots: &str) -> String {
dots.replace(".", "/")
}
fn f2s ( slashes: &str) -> String {
slashes.replace("/", ".")
}
pub fn get_class(&mut self, module: &str, class: &str) -> Vec<u8> {
let path = Self::resolve_path(module, class);
self.image.read_file(&path).unwrap()
}
}

38
src/class.rs Normal file
View File

@ -0,0 +1,38 @@
use std::sync::Arc;
use crate::attributes::AttributeInfo;
use crate::class_file::{ClassFlags, CpInfo, FieldData, FieldInfo, MethodInfo, MethodData, ClassFile};
pub struct RuntimeClass {
pub constant_pool: Arc<Vec<CpInfo>>,
pub access_flags: ClassFlags,
pub this_class: String,
pub super_class: Arc<RuntimeClass>,
pub interfaces: Vec<Arc<RuntimeClass>>,
pub fields: Vec<FieldData>,
pub methods: Vec<MethodData>,
}
impl From<ClassFile> for RuntimeClass {
fn from(value: ClassFile) -> Self {
let constant_pool = value.constant_pool.clone();
let access_flags = ClassFlags::from(value.access_flags);
Self {
constant_pool,
access_flags,
this_class: "".to_string(),
super_class: Arc::new(RuntimeClass {}),
interfaces: vec![],
fields: vec![],
methods: vec![],
}
}
}

821
src/class_file.rs Normal file
View File

@ -0,0 +1,821 @@
use deku::ctx::Endian::Big;
use std::borrow::Cow;
use std::fmt;
use std::ops::Deref;
use std::str::Chars;
use std::sync::Arc;
use itertools::Itertools;
use deku_derive::{DekuRead, DekuWrite};
use deku::{DekuContainerRead, DekuError};
use crate::attributes::{Attribute, AttributeInfo, CodeAttribute, Ops};
use crate::{BaseType, FieldType, MethodDescriptor, Value};
#[derive(Debug, PartialEq, DekuRead)]
#[deku(magic = b"\xCA\xFE\xBA\xBE", endian = "big")]
pub struct ClassFile {
pub minor_version: u16,
pub major_version: u16,
constant_pool_count: u16,
#[deku(
until = "CpInfo::weighted_count(*constant_pool_count - 1)",
map = "|v: Vec<CpInfo>| -> Result<_, DekuError> { Ok(Arc::new(v)) }"
)]
pub constant_pool: Arc<Vec<CpInfo>>,
pub access_flags: u16,
pub this_class: u16,
pub super_class: u16,
interfaces_count: u16,
#[deku(count = "interfaces_count")]
pub interfaces: Vec<u16>,
fields_count: u16,
#[deku(count = "fields_count")]
pub fields: Vec<FieldInfo>,
methods_count: u16,
#[deku(count = "methods_count")]
pub methods: Vec<MethodInfo>,
attributes_count: u16,
#[deku(count = "attributes_count")]
pub attributes: Vec<AttributeInfo>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(id_type = "u8", ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub enum CpInfo {
#[deku(id = 0x01)]
Utf8(ConstantUtf8Info),
#[deku(id = 0x03)]
Integer(i32),
#[deku(id = 0x04)]
Float(f32),
#[deku(id = 0x05)]
Long(i64),
#[deku(id = 0x06)]
Double(f64),
#[deku(id = 0x07)]
Class(ConstantClassInfo),
#[deku(id = 0x08)]
String(ConstantStringInfo),
#[deku(id = 0x09)]
FieldRef(ConstantFieldrefInfo),
#[deku(id = 10)]
MethodRef(ConstantMethodrefInfo),
#[deku(id = 11)]
InterfaceMethodRef(ConstantInterfaceMethodrefInfo),
#[deku(id = 12)]
NameAndType(ConstantNameAndTypeInfo),
#[deku(id = 15)]
MethodHandle(ConstantMethodHandleInfo),
#[deku(id = 16)]
MethodType(ConstantMethodTypeInfo),
#[deku(id = 17)]
Dynamic(ConstantDynamicInfo),
#[deku(id = 18)]
InvokeDynamic(ConstantInvokeDynamicInfo),
#[deku(id = 19)]
Module(ConstantModuleInfo),
#[deku(id = 20)]
Package(ConstantPackageInfo)
}
impl CpInfo {
fn weighted_count(target: u16) -> impl FnMut(&Self) -> bool {
let mut count = 0;
move |entry: &Self| {
count += match entry {
CpInfo::Long(_) | CpInfo::Double(_) => 2,
_ => 1,
};
count >= target as usize
}
}
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct FieldInfo {
pub access_flags: u16,
pub name_index: u16,
pub descriptor_index: u16,
attributes_count: u16,
#[deku(count = "attributes_count")]
pub attributes: Vec<AttributeInfo>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct MethodInfo {
pub access_flags: u16,
pub name_index: u16,
pub descriptor_index: u16,
attributes_count: u16,
#[deku(count = "attributes_count")]
pub attributes: Vec<AttributeInfo>,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantUtf8Info {
pub length: u16,
#[deku(count="length")]
pub bytes: Vec<u8>
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantFieldrefInfo {
pub class_index: u16,
pub name_and_type_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantInterfaceMethodrefInfo {
pub class_index: u16,
pub name_and_type_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantMethodrefInfo {
pub class_index: u16,
pub name_and_type_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantClassInfo {
pub name_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantNameAndTypeInfo {
pub name_index: u16,
pub descriptor_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantStringInfo {
pub string_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantMethodHandleInfo {
pub reference_kind: u8,
pub reference_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantMethodTypeInfo {
pub descriptor_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantDynamicInfo {
pub bootstrap_method_attr_index: u16,
pub name_and_type_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantInvokeDynamicInfo {
pub bootstrap_method_attr_index: u16,
pub name_and_type_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantModuleInfo {
pub name_index: u16,
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(ctx = "_endian: deku::ctx::Endian", endian = "big")]
pub struct ConstantPackageInfo {
pub name_index: u16,
}
// Display implementations for better formatting
impl fmt::Display for ClassFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Class File Information:")?;
writeln!(f, " Version: {}.{}", self.major_version, self.minor_version)?;
writeln!(f, " Access Flags: 0x{:04X}", self.access_flags)?;
writeln!(f, " This Class: #{}", self.this_class)?;
writeln!(f, " Super Class: #{}", self.super_class)?;
writeln!(f, "\nConstant Pool ({} entries):", self.constant_pool.len())?;
for (i, entry) in self.constant_pool().iter().enumerate() {
if let Some(entry_value) = entry
{ writeln!(f, " #{}: {}", i, entry_value)?; }
}
writeln!(f, "\nInterfaces ({}):", self.interfaces.len())?;
for interface in &self.interfaces {
writeln!(f, " #{}", interface)?;
}
writeln!(f, "\nFields ({}):", self.fields.len())?;
for (i, field) in self.fields.iter().enumerate() {
let string_name = self.get_string(field.name_index).unwrap();
writeln!(f, " [{}:{}] {}", i, string_name, field)?;
}
writeln!(f, "\nMethods ({}):", self.methods.len())?;
for (i, method) in self.methods.iter().enumerate() {
let string_name = self.get_string(method.name_index).unwrap();
writeln!(f, " [{}:{}] {}", i, string_name, method)?;
for attribute in &method.attributes {
write!(f, " ")?;
self.format_attribute(f, attribute).expect("TODO: panic message");
// writeln!(f, " {}", attribute.get(self).unwrap())?
}
}
writeln!(f, "\nAttributes ({}):", self.attributes.len())?;
for (i, attr) in self.attributes.iter().enumerate() {
writeln!(f, " [{}] name_index=#{}, length={}::: {:?}", i, attr.attribute_name_index, attr.attribute_length, attr.get(self).unwrap())?;
}
Ok(())
}
}
impl fmt::Display for CpInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CpInfo::Utf8(info) => {
let s = String::from_utf8_lossy(&info.bytes);
write!(f, "Utf8 \"{}\"", s)
}
CpInfo::Integer(val) => write!(f, "Integer {}", val),
CpInfo::Float(val) => write!(f, "Float {}", val),
CpInfo::Long(val) => write!(f, "Long {}", val),
CpInfo::Double(val) => write!(f, "Double {}", val),
CpInfo::Class(info) => write!(f, "Class #{}", info.name_index),
CpInfo::String(info) => write!(f, "String #{}", info.string_index),
CpInfo::FieldRef(info) => write!(f, "FieldRef #{}.#{}", info.class_index, info.name_and_type_index),
CpInfo::MethodRef(info) => write!(f, "MethodRef #{}.#{}", info.class_index, info.name_and_type_index),
CpInfo::InterfaceMethodRef(info) => write!(f, "InterfaceMethodRef #{}.#{}", info.class_index, info.name_and_type_index),
CpInfo::NameAndType(info) => write!(f, "NameAndType #{}:#{}", info.name_index, info.descriptor_index),
CpInfo::MethodHandle(info) => write!(f, "MethodHandle kind={} #{}", info.reference_kind, info.reference_index),
CpInfo::MethodType(info) => write!(f, "MethodType #{}", info.descriptor_index),
CpInfo::Dynamic(info) => write!(f, "Dynamic #{}.#{}", info.bootstrap_method_attr_index, info.name_and_type_index),
CpInfo::InvokeDynamic(info) => write!(f, "InvokeDynamic #{}.#{}", info.bootstrap_method_attr_index, info.name_and_type_index),
CpInfo::Module(info) => write!(f, "Module #{}", info.name_index),
CpInfo::Package(info) => write!(f, "Package #{}", info.name_index),
}
}
}
impl fmt::Display for FieldInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "flags=0x{:04X}, name=#{}, descriptor=#{}, attrs={}",
self.access_flags, self.name_index, self.descriptor_index, self.attributes.len())
}
}
impl fmt::Display for MethodInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let attrs: Vec<_> = self.attributes.iter().map(|x| { x.attribute_name_index }).collect();
write!(f, "flags=0x{:04X}, name=#{}, descriptor=#{}, attrs={}:{:?}",
self.access_flags, self.name_index, self.descriptor_index, self.attributes.len(), attrs)
}
}
impl ClassFile {
/// Parse with interpreted attributes
pub fn from_bytes_interpreted(input: (&[u8], usize)) -> Result<((&[u8], usize), Self), DekuError> {
let (rest, mut class_file) = Self::from_bytes(input)?;
// Interpret all attributes in-place
for field in &mut class_file.fields {
for attr in &mut field.attributes {
attr.interpreted = attr.parse_attribute(&class_file.constant_pool);
}
}
for method in &mut class_file.methods {
for attr in &mut method.attributes {
attr.interpreted = attr.parse_attribute(&class_file.constant_pool);
}
}
for attr in &mut class_file.attributes {
attr.interpreted = attr.parse_attribute(&class_file.constant_pool);
}
Ok((rest, class_file))
}
pub fn constant_pool(&self) -> Vec<Option<CpInfo>> {
let mut expanded = vec![None]; // Index 0 is unused in JVM
for entry in self.constant_pool.as_ref() {
expanded.push(Some(entry.clone()));
match entry {
CpInfo::Long(_) | CpInfo::Double(_) => {
expanded.push(None); // Phantom entry
}
_ => {}
}
}
expanded
}
pub fn get_constant(&self, index: u16) -> Option<&CpInfo> {
// More efficient: calculate actual index
/*let mut current_index = 1u16;
for entry in &self.constant_pool {
if current_index == index {
return Some(entry);
}
current_index += match entry {
CpInfo::Long(_) | CpInfo::Double(_) => 2,
_ => 1,
};
}
None*/
self.constant_pool.get_constant(index)
}
pub fn get_string(&self, index: u16) -> Result<String, ()> {
self.constant_pool.get_string(index)
// if let Some(CpInfo::Utf8(utf)) = self.get_constant(index) {
// return Some(String::from_utf8_lossy(&utf.bytes));
// }
// None
}
fn format_attribute(&self, f: &mut fmt::Formatter<'_>, attr: &AttributeInfo) -> fmt::Result {
let attribute = attr.get(self).unwrap_or_else(|| panic!("Failed to parse attribute {}", attr));
match &attribute {
Attribute::Code(code_attr) => {
writeln!(f, " {}", attribute)?;
for attribute in &code_attr.attributes {
write!(f, " ")?;
self.format_attribute(f, &attribute)?;
}
Ok(())
}
// Attribute::SourceFile(source_file_index) => {
// std::fmt::Display::fmt(&source_file_index, f)
// }
// Attribute::LineNumberTable(line_numbers) => {
// std::fmt::Display::fmt(&line_numbers, f)
// }
// Attribute::StackMapTable(stack_map) => {
// stack_map.fmt(f)
// }
// Attribute::Exceptions(exceptions) => {
// exceptions.fmt(f)
// }
// Attribute::InnerClasses(inner_classes) => {
// inner_classes.fmt(f)
// }
// Attribute::Signature(signature_index) => {
// std::fmt::Display::fmt(&signature_index, f)
// }
// Attribute::LocalVariableTable(local_var_table) => {
// std::fmt::Display::fmt(&local_var_table, f)
// }
Attribute::Unknown(name, data) => {
write!(f, "Unknown attribute '{}', {} bytes", name, data.len())
}
_ => {
writeln!(f, " {}", attribute)
}
}
}
pub fn get_code(&self, method_ref_data: MethodData) -> Result<CodeAttribute, ()> {
for info in self.methods.iter() {
let data = self.constant_pool.resolve_method_info(info)?;
let is_same_method_name = data.name.eq(&method_ref_data.name);
let is_same_param_desc = data.desc.parameters.eq(&method_ref_data.desc.parameters);
if is_same_method_name && is_same_param_desc {
for attr in &info.attributes {
let parsed = attr.parse_attribute(&self.constant_pool);
if let Some(Attribute::Code(attr)) = parsed {
return Ok(attr)
}
}
}
}
Err(())
}
// pub fn get_static_field_value(&self, field_ref: &FieldData) -> Value {
// for info in self.fields.iter() {
// let data = self.constant_pool.resolve_field_info(info)?;
// let is_same_field_name = data.name.eq(&field_ref.name);
// let is_same_field_desc = data.desc.eq(&method_ref_data.desc);
// if is_same_field_name && is_same_field_desc {
//
// }
// }
// }
}
pub fn pool_get_constant(constant_pool: &[CpInfo], index: u16) -> Option<&CpInfo> {
// More efficient: calculate actual index
let mut current_index = 1u16;
for entry in constant_pool {
if current_index == index {
return Some(entry);
}
current_index += match entry {
CpInfo::Long(_) | CpInfo::Double(_) => 2,
_ => 1,
};
}
None
}
pub fn pool_get_string(constant_pool: &[CpInfo], index: u16) -> Option<Cow<'_, str>> {
if let Some(CpInfo::Utf8(utf)) = pool_get_constant(constant_pool, index) {
return Some(String::from_utf8_lossy(&utf.bytes));
}
None
}
#[derive(Clone, PartialEq, Debug, DekuRead)]
#[deku(endian = "Big")]
pub(crate) struct Bytecode {
bytes: u32,
#[deku(bytes_read = "bytes")]
pub code: Vec<Ops>,
}
type CONSTANT_POOL = [CpInfo];
pub trait ConstantPoolExt {
fn get_constant(&self, index: u16) -> Option<&CpInfo>;
fn get_string(&self, index: u16) -> Result<String, ()>;
fn get_field(&self, index: u16) -> Result<&ConstantFieldrefInfo, ()>;
fn get_class(&self, index: u16) -> Result<&ConstantClassInfo, ()>;
fn get_name_and_type(&self, index: u16) -> Result<&ConstantNameAndTypeInfo, ()>;
fn resolve_field(&self, index: u16) -> Result<FieldData, ()>;
fn resolve_method_ref(&self, index: u16) -> Result<MethodData, ()>;
fn resolve_method_info(&self, method: &MethodInfo) -> Result<MethodData, ()>;
fn resolve_field_info(&self, field: &FieldInfo) -> Result<FieldData, ()>;
}
impl ConstantPoolExt for [CpInfo] {
fn get_constant(&self, index: u16) -> Option<&CpInfo> {
let mut current_index = 1u16;
for entry in self {
if current_index == index {
return Some(entry);
}
current_index += match entry {
CpInfo::Long(_) | CpInfo::Double(_) => 2,
_ => 1,
};
}
None
}
fn get_string(&self, index: u16) -> Result<String, ()> {
let cp_entry = self.get_constant(index).ok_or(())?;
match cp_entry {
CpInfo::Utf8(data) => {
String::from_utf8(data.bytes.clone()).map_err(|e| ())
},
_ => Err(()),
}
}
fn get_field(&self, index: u16) -> Result<&ConstantFieldrefInfo, ()> {
let cp_entry = self.get_constant(index).ok_or(())?;
match cp_entry {
CpInfo::FieldRef(data) => Ok(data),
_ => Err(()),
}
}
fn get_class(&self, index: u16) -> Result<&ConstantClassInfo, ()> {
let cp_entry = self.get_constant(index).ok_or(())?;
match cp_entry {
CpInfo::Class(data) => Ok(data),
_ => Err(()),
}
}
fn get_name_and_type(&self, index: u16) -> Result<&ConstantNameAndTypeInfo, ()> {
let cp_entry = self.get_constant(index).ok_or(())?;
match cp_entry {
CpInfo::NameAndType(data) => Ok(data),
_ => Err(()),
}
}
fn resolve_field(&self, index: u16) -> Result<FieldData, ()> {
if let Some(CpInfo::FieldRef(fr)) = self.get_constant(index) {
let class = self.get_class(fr.class_index)?;
let class = self.get_string(class.name_index)?;
let name_and_type = self.get_name_and_type(fr.name_and_type_index)?;
let name = self.get_string(name_and_type.name_index)?;
let desc = self.get_string(name_and_type.descriptor_index)?;
let desc = FieldType::parse(&desc)?;
Ok(FieldData {
class,
name,
desc,
})
} else { Err(()) }
}
fn resolve_method_ref(&self, index: u16) -> Result<MethodData, ()> {
if let Some(CpInfo::MethodRef(mr)) = self.get_constant(index) {
let class = self.get_class(mr.class_index)?;
let class = self.get_string(class.name_index)?;
let name_and_type = self.get_name_and_type(mr.name_and_type_index)?;
let name = self.get_string(name_and_type.name_index)?;
let desc = self.get_string(name_and_type.descriptor_index)?;
let desc = MethodDescriptor::parse(&desc)?;
Ok(MethodData {
class,
name,
desc,
})
} else { Err(()) }
}
// (name, desc)
fn resolve_method_info(&self, method: &MethodInfo) -> Result<MethodData, ()> {
let desc = self.get_string(method.descriptor_index)?;
let desc = MethodDescriptor::parse(&desc)?;
let name = self.get_string(method.name_index)?;
Ok(MethodData {
class: "".to_string(),
name,
desc,
})
}
fn resolve_field_info(&self, field: &FieldInfo) -> Result<FieldData, ()> {
let desc = self.get_string(field.descriptor_index)?;
let desc = FieldType::parse(&desc)?;
let name = self.get_string(field.name_index)?;
Ok(FieldData {
class: "".to_string(),
name,
desc,
})
}
}
struct ConstantPool<'a>(&'a [CpInfo]);
impl Deref for ConstantPool {
type Target = [CpInfo];
fn deref(&self) -> &Self::Target {
self.0
}
}
#[derive(Debug)]
pub struct MethodData {
pub class: String,
pub name: String,
pub desc: MethodDescriptor,
pub code: Option<Bytecode>
}
#[derive(Debug)]
pub struct FieldData {
pub class: String,
pub name: String,
pub desc: FieldType
}
#[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
pub struct ClassFlags {
// flags
#[deku(bits = 1)]
MODULE: bool,
#[deku(bits = 1)]
ENUM: bool,
#[deku(bits = 1)]
ANNOTATION: bool,
#[deku(bits = 1)]
SYNTHETIC: bool,
#[deku(bits = 1, pad_bits_before = "1")]
ABSTRACT: bool,
#[deku(bits = 1)]
INTERFACE: bool,
#[deku(bits = 1, pad_bits_before = "3")]
SUPER: bool,
#[deku(bits = 1)]
FINAL: bool,
#[deku(bits = 1, pad_bits_before = "3")]
PUBLIC: bool,
}
impl From<u16> for ClassFlags {
fn from(value: u16) -> Self {
let (_rem, flags) = Self::from_bytes((&value.to_be_bytes(), 0)).unwrap();
flags
}
}
#[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
pub struct ModuleFlags {
#[deku(bits = 1)]
ACC_MANDATED: bool,
#[deku(bits = 1, pad_bits_before = "2")]
ACC_SYNTHETIC: bool,
#[deku(bits = 1, pad_bits_before = "5")]
ACC_STATIC_PHASE: bool,
#[deku(bits = 1, pad_bits_after = "5")]
ACC_TRANSITIVE: bool,
}
impl From<u16> for ModuleFlags {
fn from(value: u16) -> Self {
let (_rem, flags) = Self::from_bytes((&value.to_be_bytes(), 0)).unwrap();
flags
}
}
#[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
pub struct FieldFlags {
#[deku(bits = 1, pad_bits_before = "1")]
ACC_ENUM: bool,
#[deku(bits = 1, pad_bits_before = "1")]
ACC_SYNTHETIC: bool,
#[deku(bits = 1, pad_bits_before = "4")]
ACC_TRANSIENT: bool,
#[deku(bits = 1)]
ACC_VOLATILE: bool,
#[deku(bits = 1, pad_bits_before = "1")]
ACC_FINAL: bool,
#[deku(bits = 1)]
ACC_STATIC: bool,
#[deku(bits = 1)]
ACC_PROTECTED: bool,
#[deku(bits = 1)]
ACC_PRIVATE: bool,
#[deku(bits = 1)]
ACC_PUBLIC: bool,
}
impl From<u16> for FieldFlags {
fn from(value: u16) -> Self {
let (_rem, flags) = Self::from_bytes((&value.to_be_bytes(), 0)).unwrap();
flags
}
}
#[allow(non_snake_case)]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
pub struct MethodFlags {
#[deku(bits = 1, pad_bits_before = "3")]
ACC_SYNTHETIC: bool,
#[deku(bits = 1)]
ACC_STRICT: bool,
#[deku(bits = 1)]
ACC_ABSTRACT: bool,
#[deku(bits = 1, pad_bits_before = "1")]
ACC_NATIVE: bool,
#[deku(bits = 1)]
ACC_VARARGS: bool,
#[deku(bits = 1)]
ACC_BRIDGE: bool,
#[deku(bits = 1)]
ACC_SYNCHRONIZED: bool,
#[deku(bits = 1)]
ACC_FINAL: bool,
#[deku(bits = 1)]
ACC_STATIC: bool,
#[deku(bits = 1)]
ACC_PROTECTED: bool,
#[deku(bits = 1)]
ACC_PRIVATE: bool,
#[deku(bits = 1)]
ACC_PUBLIC: bool,
}
impl From<u16> for MethodFlags {
fn from(value: u16) -> Self {
let (_rem, flags) = Self::from_bytes((&value.to_be_bytes(), 0)).unwrap();
flags
}
}
//yoinked because im monkled
impl MethodDescriptor {
/// Parses a method descriptor as specified in the JVM specs:
/// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3
pub fn parse(descriptor: &str) -> Result<MethodDescriptor, ()> {
let mut chars = descriptor.chars();
match chars.next() {
Some('(') => {
let parameters = Self::parse_parameters(descriptor, &mut chars)?;
if Some(')') == chars.next() {
let return_type = Self::parse_return_type(descriptor, &mut chars)?;
Ok(MethodDescriptor {
parameters,
return_type,
})
} else {
Err(())
}
}
_ => Err(()),
}
}
fn parse_parameters(
descriptor: &str,
chars: &mut Chars,
) -> Result<Vec<FieldType>, ()> {
let mut parameters = Vec::new();
loop {
match chars.clone().next() {
Some(')') => return Ok(parameters),
Some(_) => {
let param = FieldType::parse_from(descriptor, chars)?;
parameters.push(param);
}
None => return Err(()),
}
}
}
fn parse_return_type(
descriptor: &str,
chars: &mut Chars,
) -> Result<Option<FieldType>, ()> {
match chars.clone().next() {
Some('V') => Ok(None),
Some(_) => {
let return_type = Some(FieldType::parse_from(descriptor, chars)?);
if chars.next().is_none() {
Ok(return_type)
} else {
Err(())
}
}
_ => Err(()),
}
}
pub fn num_arguments(&self) -> usize {
self.parameters.len()
}
}
impl FieldType {
pub fn parse(type_descriptor: &str) -> Result<FieldType, ()> {
let mut chars = type_descriptor.chars();
let descriptor = Self::parse_from(type_descriptor, &mut chars)?;
match chars.next() {
None => Ok(descriptor),
Some(_) => Err(()),
}
}
pub(crate) fn parse_from(
type_descriptor: &str,
chars: &mut Chars,
) -> Result<FieldType, ()> {
let first_char = chars
.next()
.ok_or(())?;
Ok(match first_char {
'B' => FieldType::Base(BaseType::Byte),
'C' => FieldType::Base(BaseType::Char),
'D' => FieldType::Base(BaseType::Double),
'F' => FieldType::Base(BaseType::Float),
'I' => FieldType::Base(BaseType::Int),
'J' => FieldType::Base(BaseType::Long),
'S' => FieldType::Base(BaseType::Short),
'Z' => FieldType::Base(BaseType::Boolean),
'L' => {
let class_name: String = chars.take_while_ref(|c| *c != ';').collect();
match chars.next() {
Some(';') => FieldType::ClassType(class_name),
_ => return Err(()),
}
}
'[' => {
let component_type = Self::parse_from(type_descriptor, chars)?;
FieldType::ArrayType(Box::new(component_type))
}
_ => return Err(()),
})
}
}

317
src/class_loader.rs Normal file
View File

@ -0,0 +1,317 @@
use std::collections::hash_map::{Entry, Iter};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use log::warn;
use crate::bimage::Bimage;
use crate::class::RuntimeClass;
use crate::class_file::{ClassFile, ClassFlags, ConstantClassInfo, ConstantPoolExt, CpInfo};
pub type LoaderRef = Arc<Mutex<ClassLoader>>;
#[deprecated(
note = "This method is deprecated and will be removed in future versions"
)]
pub fn resolve_path(what: &str) -> Result<(PathBuf, String), String> {
let (module, fqn) = what.split_once("/").unwrap_or(("", what));
let module = dot_to_path(module);
let fqn = dot_to_path(fqn);
let base = Path::new("./data");
if !base.exists() {
return Err("Could not find data directory!".to_string());
}
let path = if !module.is_empty() {
let module_path = base.join(&module);
if !module_path.exists() {
return Err(format!("Could not find module: {}", module));
}
let classes_path = module_path.join("jmod/classes");
if !classes_path.exists() {
return Err(format!("Could not find jmod/classes directory in module: {}", module));
}
classes_path
} else { base.to_path_buf() };
let class_path = path.join(format!("{}.class", fqn));
if !class_path.exists() {
return Err(format!("Could not find class: {} in module: {}", fqn, module));
}
Ok((class_path, path_to_dot(&fqn)))
}
/// A struct representing a ClassLoader which is responsible for managing and storing classes and their associated binary image information.
///
/// # Fields
///
/// - `classes`: A `HashMap<String, ClassFile>` that maps class names (as `String`) to their corresponding `ClassFile` instances.
/// This serves as the storage for loaded classes.
///
/// - `bimage`: A `Bimage` instance used for handling binary image data associated with the class loading process.
///
/// # Derives
///
/// - `Default`: The `ClassLoader` can be instantiated with default values using the `Default` trait.
/// By default, `classes` will be an empty `HashMap` and `bimage` will use its own default initialization.
///
/// # Usage
///
/// The `ClassLoader` struct is useful in environments or applications that require dynamic
/// or on-demand loading of classes with related binary image data.
///
/// Example:
/// ```
/// use std::collections::HashMap;
/// let class_loader = ClassLoader::default();
/// assert!(class_loader.classes.is_empty());
/// ```
#[derive(Default)]
pub struct ClassLoader {
classes: HashMap<String, Arc<ClassFile>>,
bimage: Bimage,
}
impl ClassLoader {
pub fn new() -> Result<Self, String> {
let mut loader = Self::default();
// for entry in VM_CLASSES {
// let module_class = format!("{}/{}", "java.base", entry);;
// loader.load_class(&module_class)?;
// }
Ok(loader)
}
/// Retrieves an `Arc<ClassFile>` from the internal storage, or attempts to load it if not already present.
///
/// # Arguments
/// * `what` - A fully qualified class name (e.g. "java.base/java.lang.Object" or "java.lang.String")
///
/// # Returns
/// * `Ok(Arc<ClassFile>)` - A thread-safe reference-counted pointer to the requested `ClassFile` on success,
/// either retrieved from the storage or successfully loaded.
/// * `Err(String)` - An error message string if the class could not be loaded due to some failure.
///
/// # Behavior
/// * Checks if the requested class is already present in the internal `classes` storage.
/// * If the class is not present, it attempts to load the class by calling `self.load_class()`.
/// * If the class loading fails, the function returns an error.
/// * If successful, it retrieves the `Arc<ClassFile>` from the storage and returns a clone of it.
/// * Using `Arc` allows the ClassFile to be shared between multiple threads safely.
///
/// # Example
/// ```rust
/// # use std::sync::Arc;
/// let mut class_loader = ClassLoader::new()?;
/// match class_loader.get_or_load("java.base/java.lang.Object") {
/// Ok(class_file) => {
/// // class_file is an Arc<ClassFile> that can be cloned and shared
/// let shared_class = class_file.clone();
/// }
/// Err(e) => {
/// eprintln!("Failed to load class: {}", e);
/// }
/// }
/// ```
pub fn get_or_load(&mut self, what: &str) -> Result<Arc<ClassFile>, String> {
if let Some(class) = self.classes.get(what) {
return Ok(class.clone());
}
let class = self.load_class(what)?;
self.classes.insert(what.to_string(), class.clone());
Ok(class)
}
/* pub fn classes(&self) -> HashMap<String, ClassFile> {
self.classes.clone()
}*/
fn load_class(&mut self, what: &str) -> Result<Arc<ClassFile>, String> {
let (module, class_fqn) = what.split_once("/").unwrap_or(("", what));
let bytes = self.bimage.get_class(module, class_fqn);
let (_, cf) = ClassFile::from_bytes_interpreted((bytes.as_ref(), 0))
.map_err(|e| format!("failed to parse class file: {}", e))?;
let arced = Arc::new(cf);
let option = self.classes.insert(class_fqn.to_string(), arced.clone());
if option.is_some() { warn!("Replaced loaded class: {}", class_fqn) }
Ok(arced)
}
fn runtime_class(&self, class_file: ClassFile) -> RuntimeClass {
let constant_pool = class_file.constant_pool.clone();
let access_flags = ClassFlags::from(class_file.access_flags);
let this_class = {
let this_class_info = class_file.constant_pool.get_constant(class_file.this_class)
if let Some(CpInfo::Class(class_info)) = this_class_info {
class_file.constant_pool.get_string(class_info.name_index)
}
}
RuntimeClass {
constant_pool,
access_flags,
this_class: "".to_string(),
super_class: Arc::new(RuntimeClass {}),
interfaces: vec![],
fields: vec![],
methods: vec![],
}
}
}
fn dot_to_path(s: &str) -> String {
s.replace(".", "/")
}
pub fn path_to_dot(s: &str) -> String {
s.replace("/", ".")
}
const VM_CLASSES: &[&str] = &[
"java.lang.Object",
"java.lang.String",
"java.lang.Class",
"java.lang.Cloneable",
"java.lang.ClassLoader",
"java.io.Serializable",
"java.lang.System",
"java.lang.Throwable",
"java.lang.Error",
"java.lang.Exception",
// "java.lang.RuntimeException",
// "java.security.ProtectionDomain",
// "java.security.SecureClassLoader",
// "java.lang.ClassNotFoundException",
// "java.lang.Record",
// "java.lang.NoClassDefFoundError",
// "java.lang.LinkageError",
// "java.lang.ClassCastException",
// "java.lang.ArrayStoreException",
// "java.lang.VirtualMachineError",
// "java.lang.InternalError",
// "java.lang.OutOfMemoryError",
// "java.lang.StackOverflowError",
// "java.lang.IllegalMonitorStateException",
// "java.lang.ref.Reference",
// "java.lang.IllegalCallerException",
// "java.lang.ref.SoftReference",
// "java.lang.ref.WeakReference",
// "java.lang.ref.FinalReference",
// "java.lang.ref.PhantomReference",
// "java.lang.ref.Finalizer",
// "java.lang.Thread",
// "java.lang.Thread.FieldHolder",
// "java.lang.Thread.Constants",
// "java.lang.ThreadGroup",
// "java.lang.BaseVirtualThread",
// "java.lang.VirtualThread",
// "java.lang.BoundVirtualThread",
// "java.util.Properties",
// "java.lang.Module",
// "java.lang.reflect.AccessibleObject",
// "java.lang.reflect.Field",
// "java.lang.reflect.Parameter",
// "java.lang.reflect.Method",
// "java.lang.reflect.Constructor",
// "java.lang.Runnable",
// "jdk.internal.vm.ContinuationScope",
// "jdk.internal.vm.Continuation",
// "jdk.internal.vm.StackChunk",
// "reflect.MethodAccessorImpl",
// "reflect.ConstantPool",
// "reflect.CallerSensitive",
// "reflect.DirectConstructorHandleAccessor.NativeAccessor",
// "java.lang.invoke.DirectMethodHandle",
// "java.lang.invoke.MethodHandle",
// "java.lang.invoke.VarHandle",
// "java.lang.invoke.MemberName",
// "java.lang.invoke.ResolvedMethodName",
// "java.lang.invoke.MethodHandleImpl",
// "java.lang.invoke.MethodHandleNatives",
// "java.lang.invoke.LambdaForm",
// "java.lang.invoke.MethodType",
// "java.lang.BootstrapMethodError",
// "java.lang.invoke.CallSite",
// "jdk.internal.foreign.abi.NativeEntryPoint",
// "jdk.internal.foreign.abi.ABIDescriptor",
// "jdk.internal.foreign.abi.VMStorage",
// "jdk.internal.foreign.abi.CallConv",
// "java.lang.invoke.ConstantCallSite",
// "java.lang.invoke.MutableCallSite",
// "java.lang.invoke.VolatileCallSite",
// "java.lang.AssertionStatusDirectives",
// "java.lang.StringBuffer",
// "java.lang.StringBuilder",
// "jdk.internal.misc.UnsafeConstants",
// "jdk.internal.misc.Unsafe",
// "jdk.internal.module.Modules",
// "java.io.ByteArrayInputStream",
// "java.net.URL",
// "java.lang.Enum",
// "java.util.jar.Manifest",
// "jdk.internal.loader.BuiltinClassLoader",
// "jdk.internal.loader.ClassLoaders",
// "jdk.internal.loader.ClassLoaders.AppClassLoader",
// "jdk.internal.loader.ClassLoaders.PlatformClassLoader",
// "java.security.CodeSource",
// "java.util.concurrent.ConcurrentHashMap",
// "java.util.ArrayList",
// "java.lang.StackTraceElement",
// "java.nio.Buffer",
// "java.lang.StackWalker",
// "java.lang.StackStreamFactory.AbstractStackWalker",
// "java.lang.ClassFrameInfo",
// "java.lang.StackFrameInfo",
// "java.lang.LiveStackFrameInfo",
// "java.util.concurrent.locks.AbstractOwnableSynchronizer",
// "java.lang.Boolean",
// "java.lang.Character",
// "java.lang.Float",
// "java.lang.Double",
// "java.lang.Byte",
// "java.lang.Short",
// "java.lang.Integer",
// "java.lang.Long",
// "java.lang.Void",
// "java.util.Iterator",
// "java.lang.reflect.RecordComponent",
// "jdk.internal.vm.vector.VectorSupport",
// "jdk.internal.vm.vector.VectorPayload",
// "jdk.internal.vm.vector.Vector",
// "jdk.internal.vm.vector.VectorMask",
// "jdk.internal.vm.vector.VectorShuffle",
// "jdk.internal.vm.FillerObject",
];
// fn load_class(module: &str, fqn: &str) -> Result<ClassFile, String> {
// let path = resolve_path(module, fqn)
// .map_err(|e| format!("failed to resolve class file location: {}", e))?;
// let mut class_file = File::open(path)
// .map_err(|e| format!("failed to open class file: {}", e))?;
// let mut bytes = Vec::new();
// class_file.read_to_end(&mut bytes)
// .map_err(|e| format!("failed to read class file: {}", e))?;
// let (_, cf) = ClassFile::from_bytes_interpreted((bytes.as_ref(), 0))
// .map_err(|e| format!("failed to parse class file: {}", e))?;
// Ok(cf)
// }

373
src/lib.rs Normal file
View File

@ -0,0 +1,373 @@
use std::cell::RefCell;
use std::io::Read;
use std::fmt::{Debug, Display, Formatter};
use std::fs::File;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use deku::DekuContainerRead;
use deku_derive::{DekuRead, DekuWrite};
use itertools::Itertools;
use vm::Vm;
use crate::attributes::{Attribute, CodeAttribute, Ops};
use crate::class_file::{Bytecode, ClassFile, ClassFlags, ConstantPoolExt, CpInfo, FieldFlags, MethodFlags};
use crate::JVM_ACC::*;
use crate::object::Object;
use crate::thread::VmThread;
mod attributes;
mod class_file;
mod class_loader;
mod macros;
mod bimage;
mod vm;
mod object;
mod rng;
mod thread;
mod class;
const NULL: Value = Value::Reference(None);
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
pub fn run() {
env_logger::init();
// let mut cl = ClassLoader::new().unwrap();
// cl.load_class("org.example.App").expect("TODO: panic message");
// let clazz = cl.get_or_load("org.example.App").unwrap();
// for (i, (k, v)) in cl.classes().iter().enumerate() {
// std::fs::write(format!("./output/{}-{}.txt", i, class_loader::path_to_dot(k)), format!("{}\n{}", k, v)).unwrap();
// }
let mut class_file = File::open("./data/org/example/App.class").unwrap();
let mut bytes = Vec::new();
class_file.read_to_end(&mut bytes).unwrap();
let (_rest, clazz) = ClassFile::from_bytes((bytes.as_ref(), 0)).unwrap();
let method = clazz.methods.iter().nth(1).unwrap().clone();
let code = method.attributes.iter().find_map(|x| {
if let Some(Attribute::Code(code_attr)) = &x.get(&clazz) {
Some(code_attr.clone())
} else {
None
}
}).unwrap();
// let frame = Frame::new();
// println!("{}", code);
let mut buf = Vec::new();
let bytes = code.code_length.to_be_bytes();
buf.extend_from_slice(&bytes);
buf.extend_from_slice(&code.code.clone());
let (_rest, ops) = Bytecode::from_bytes((buf.as_ref(), 0)).unwrap();
let var_table = code.attributes.iter().find_map(|x| {
if let Some(Attribute::LocalVariableTable(varTableAttr)) = &x.get(&clazz) {
Some(varTableAttr.clone())
} else {
None
}
}).unwrap();
println!("{}", clazz);
let pool = clazz.constant_pool;
let mut vm = Arc::new(Vm::new());
let mut frame = Frame::new(code, pool, Default::default(), vm);
// println!("{:?}", frame);
frame.execute();
// println!("{:?}", ops);
// println!("{:?}", var_table.local_variable_table);
// vm.method(ops.clone(), code, var_table);
}
#[derive(Debug)]
pub struct KlassField {
pub name: String,
pub field_flags: FieldFlags,
pub descriptor: FieldType,
}
#[derive(Debug)]
pub struct KlassMethod {
pub name: String,
pub method_flags: MethodFlags,
pub method_descriptor: MethodDescriptor,
pub code_attribute: CodeAttribute
}
type ObjectRef = Arc<Mutex<Object>>;
#[derive(Debug)]
#[derive(Clone)]
enum Value {
Boolean(bool),
Char(char),
Float(f32),
Double(f64),
Byte(i8),
Short(i16),
Int(i32),
Long(i64),
Reference(Option<ObjectRef>),
}
struct Frame {
// program counter
pc: u16,
// operand stack
stack: Vec<Value>,
// local vars
vars: Vec<Value>,
// constant pool
pool: Arc<Vec<CpInfo>>,
bytecode: Bytecode,
thread: Arc<VmThread>
}
impl Display for Frame {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "PC: {}\nStack: {:?}\nVars: {:?}", self.pc, self.stack, self.vars)
}
}
// println!("State:\n\tStack: {:?}\n\tLocals :{:?}\n", self.stack, self.vars) }
impl Frame {
fn load_constant(index: u8) {}
fn new(code_attr: CodeAttribute, pool: Arc<Vec<CpInfo>>, mut locals: Vec<Value>, thread: Arc<VmThread>) -> Self {
let max_stack = code_attr.max_stack as usize;
let max_local = code_attr.max_locals as usize;
let bytes = code_attr.code_length.to_be_bytes();
let mut buf = Vec::new();
buf.extend_from_slice(&bytes);
buf.extend_from_slice(&code_attr.code.clone());
let (_rest, bytecode) = Bytecode::from_bytes((buf.as_ref(), 0)).unwrap();
let extend = vec![Value::Reference(None); max_local-locals.len()];
locals.extend_from_slice(&extend);
Frame {
pc: 0,
stack: Vec::with_capacity(max_stack),
vars: locals,
pool,
bytecode,
vm,
}
}
fn execute(&mut self) {
let binding = self.bytecode.code.clone();
let mut ops = binding.iter();
while let Some(op) = ops.next() {
println!("Executing Op: {:?}", op);
let result = self.execute_instruction(op);
match result {
Ok(_) => { println!("State:\n\tStack: {:?}\n\tLocals :{:?}\n", self.stack, self.vars) }
Err(_) => {panic!("Mission failed, we'll get em next time")}
}
}
()
}
}
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(id_type = "u8")]
#[deku(seek_from_current = "-1")]
pub enum BaseType {
/// B
#[deku(id = "0x42")]
Byte,
/// C
#[deku(id = "0x43")]
Char,
/// D
#[deku(id = "0x44")]
Double,
/// F
#[deku(id = "0x46")]
Float,
/// I
#[deku(id = "0x49")]
Int,
/// J
#[deku(id = "0x4A")]
Long,
/// S
#[deku(id = "0x53")]
Short,
/// Z
#[deku(id = "0x5A")]
Boolean,
}
impl From<char> for BaseType {
fn from(value: char) -> Self {
match value {
'B' => BaseType::Byte,
'C' => BaseType::Char,
'D' => BaseType::Double,
'F' => BaseType::Float,
'I' => BaseType::Int,
'J' => BaseType::Long,
'S' => BaseType::Short,
'Z' => BaseType::Boolean,
_ => panic!("Invalid base type: {}", value)
}
}
}
#[derive(Debug, PartialEq)]
pub struct MethodDescriptor {
parameters: Vec<FieldType>,
// none = void/v
return_type: Option<FieldType>
}
#[derive(Debug, PartialEq)]
pub enum FieldType {
Base(BaseType),
// L ClassName
ClassType(String),
// [ ComponentType
ArrayType(Box<FieldType>),
}
enum ExecutionResult {
Continue,
Return(()),
ReturnValue(Value)
}
impl Frame {
fn execute_instruction(&mut self, op: &Ops) -> Result<ExecutionResult, ()> {
match op {
Ops::ldc(index) => {
let thing = self.pool.get_constant(index.to_owned() as u16).ok_or(())?;
println!("\tLoading constant: {}", thing);
let resolved: Option<Value> = match thing {
CpInfo::Utf8(x) => { println!("{:?}", String::from_utf8(x.bytes.clone())); None }
CpInfo::Integer(x) => { Some(Value::Int(x.clone())) }
CpInfo::Float(x) => { Some(Value::Float(x.clone())) }
// CpInfo::Long(x) => { None }
// CpInfo::Double(x) => {None}
CpInfo::Class(x) => {None}
CpInfo::String(x) => {
// self.vm.
None
}
// CpInfo::FieldRef(x) => {}
CpInfo::MethodRef(x) => {None}
// CpInfo::InterfaceMethodRef(x) => {}
// CpInfo::NameAndType(x) => {}
CpInfo::MethodHandle(x) => {None}
CpInfo::MethodType(x) => { None }
// CpInfo::Dynamic(x) => {}
// CpInfo::InvokeDynamic(x) => {}
// CpInfo::Module(x) => {}
// CpInfo::Package(x) => {}
_ => { None }
};
if let Some(x) = resolved {
self.stack.push(x);
};
Ok(ExecutionResult::Continue)
},
Ops::ldc2_w(index) => {
let val = self.pool.get_constant(*index).ok_or(())?;
println!("\tLoading constant: {}", val);
let resolved = match val {
CpInfo::Double(x) => { Some(Value::Double(x.clone())) }
CpInfo::Long(x) => { Some(Value::Long(x.clone())) }
_ => { None }
};
if let Some(x) = resolved {
self.stack.push(x);
};
Ok(ExecutionResult::Continue)
}
// store
Ops::fstore(index) => { store!(self, f, *index as usize) },
Ops::fstore_0 => { store!(self, f, 0) },
Ops::fstore_1 => { store!(self, f, 1) },
Ops::fstore_2 => { store!(self, f, 2) },
Ops::fstore_3 => { store!(self, f, 3) }
Ops::dstore(index) => { store!(self, d, *index as usize) },
Ops::dstore_0 => { store!(self, d, 0) },
Ops::dstore_1 => { store!(self, d, 1) },
Ops::dstore_2 => { store!(self, d, 2) },
Ops::dstore_3 => { store!(self, d, 3) }
// load
Ops::fload(index) => { load!(self, f, *index as usize) }
Ops::fload_0 => { load!(self, f, 0) }
Ops::fload_1 => { load!(self, f, 1) }
Ops::fload_2 => { load!(self, f, 2) }
Ops::fload_3 => { load!(self, f, 3) }
Ops::dload(index) => { load!(self, d, *index as usize) }
Ops::dload_0 => { load!(self, d, 0) }
Ops::dload_1 => { load!(self, d, 1) }
Ops::dload_2 => { load!(self, d, 2) }
Ops::dload_3 => { load!(self, d, 3) }
Ops::f2d => {
if let Value::Float(float) = self.stack.pop().expect("Stack must have value") {
let double: f64 = float.into();
self.stack.push(Value::Double(double));
Ok(ExecutionResult::Continue)
} else { Err(()) }
}
Ops::dadd => {
let value1 = self.stack.pop().expect("Stack must have value");
let value2 = self.stack.pop().expect("Stack must have value");
if let (Value::Double(value1), Value::Double(value2)) = (value1, value2) {
self.stack.push(Value::Double(value1 + value2));
Ok(ExecutionResult::Continue)
} else { Err(()) }
}
// get static field
Ops::getstatic(index) => {
let field = self.pool.resolve_field(*index)?;
// let (code, pool) = {
// let mut loader = self.vm.loader.lock().unwrap();
// let class = loader.get_or_load(&field.class).unwrap();
// let field = class.get_static_field_value(&field)
// // let code = class.get_code(meth)?;
// (code, pool)
// };
println!("{:?}", field);
todo!("Finish get static");
Ok(ExecutionResult::Continue)
},
Ops::invokevirtual(index) => {
let meth = self.pool.resolve_method_ref(*index)?;
let params = meth.desc.num_arguments();
let last = self.stack.len() - 1;
let first = last - params + 1;
let slice = self.stack.get(first..last).unwrap().to_vec();
//sub slice param length + one, throw it to frame new
let (code, pool) = {
let mut loader = self.vm.loader.lock().unwrap();
let class = loader.get_or_load(&meth.class).unwrap();
let pool = class.constant_pool.clone();
let code = class.get_code(meth)?;
(code, pool)
};
// let code = class.get_code(meth)?;
// let class = self.vm.loader.get_or_load(&meth.class).unwrap();
// let pool = &class.constant_pool;
let vars = slice;
let frame = Frame::new(code, pool.clone(), vars, self.vm.clone());
// println!("{:?}", meth);
// todo!("Finish invoke virtual");
Ok(ExecutionResult::Continue)
}
Ops::return_void => {
Ok(ExecutionResult::Return(()))
}
_ => { todo!("Unimplemented op: {:?}", op) }
}
}
}

42
src/macros.rs Normal file
View File

@ -0,0 +1,42 @@
use crate::Value;
#[macro_export]
macro_rules! store {
($self:expr, l, $index:expr) => {
{{
let index: usize = $index;
let value = $self.stack.pop().expect("Must contain value on stack");
println!("\tStoring: {value:?} into local[{index}]");
$self.vars[index] = value;
$self.vars[index + 1] = Value::Reference(None);
Ok(ExecutionResult::Continue)
}}
};
($self:expr, d, $index:expr) => {store!($self, l, $index)};
($self:expr, i, $index:expr) => {
{{
let index: usize = $index;
let value = $self.stack.pop().expect("Must contain value on stack");
println!("\tStoring: {value:?} into local[{index}]");
$self.vars[index] = value;
Ok(ExecutionResult::Continue)
}}
};
($self:expr, f, $index:expr) => {store!($self, i, $index)};
}
#[macro_export]
macro_rules! load {
($self:expr, i, $index:expr) => {
{{
let index: usize = $index;
let value = $self.vars.get(index).expect("Local var to exist");
println!("\tLoading: local[{index}] - {value:?} onto stack");
$self.stack.push(value.clone());
Ok(ExecutionResult::Continue)
}}
};
($self:expr, d, $index:expr) => {load!($self, l, $index)};
($self:expr, l, $index:expr) => {load!($self, i, $index)};
($self:expr, f, $index:expr) => {load!($self, i, $index)};
}

View File

@ -1,84 +1,5 @@
mod primatives;
use std::fs;
use std::fs::File;
use std::io::Read;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::__lib::SpacetimeType;
use spacetimedb_sdk::__codegen::__sats::bsatn;
use deku_derive
fn main() {
let mut class_file = File::open("./data/main.class").unwrap();
let mut bytes = Vec::new();
class_file.read_to_end(&mut bytes).unwrap();
let cf = bsatn::from_slice::<ClassFile>(&*bytes).unwrap();
println!("{:?}", cf);
}
#[deku(magic="")]
pub struct ClassFile {
pub magic: u32,
pub minor_version: u16,
pub major_version: u16,
pub constant_pool: Vec<CpInfo>, // Note: count is pool.len() + 1
pub access_flags: u16,
pub this_class: u16,
pub super_class: u16,
pub interfaces: Vec<u16>,
pub fields: Vec<FieldInfo>,
pub methods: Vec<MethodInfo>,
pub attributes: Vec<AttributeInfo>,
}
// Placeholder types - you'd need to define these based on the JVM spec
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub enum CpInfo {
NULL, // i think this is needed?
Utf8(&'static str),
NULLTWO, // needed again i think?
Integer(i32),
Float(f32),
Long(i64),
Double(f64),
Class(u16),
String(u16),
FieldRef,
MethodRef(u16, u16),
InterfaceMethodRef,
NameAndType,
NULLTHIRTEEN,
NULLFOURTEEN,
MethodHandle,
MethodType,
NULLSEVENTEEN,
InvokeDynamic,
Module,
Package
}
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct FieldInfo {
// Field structure
}
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct MethodInfo {
// Method structure
}
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct AttributeInfo {
// Attribute structure
}
pub struct ConstantUtf8Info {
length: u16,
bytes: [u8],
vec: Vec<str>
jvm_rs::run()
}

19
src/object.rs Normal file
View File

@ -0,0 +1,19 @@
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use crate::class_file::ClassFile;
use crate::Value;
#[derive(Debug, Clone)]
pub struct Object {
pub id: String,
pub class: Arc<ClassFile>,
fields: Arc<Mutex<Vec<Value>>>,
}
impl PartialEq for Object {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Object {}

69
src/rng.rs Normal file
View File

@ -0,0 +1,69 @@
use std::cell::Cell;
use std::sync::atomic::{AtomicU32, Ordering};
static RAND_SEED: AtomicU32 = AtomicU32::new(1234567);
fn next_random(rand_seed: u32) -> u32 {
const A: u32 = 16807;
const M: u32 = 2147483647; // 2^31 - 1
// compute az=2^31p+q
let mut lo = A * (rand_seed & 0xFFFF);
let hi = A * (rand_seed >> 16);
lo += (hi & 0x7FFF) << 16;
// if q overflowed, ignore the overflow and increment q
if lo > M {
lo &= M;
lo += 1;
}
lo += hi >> 15;
// if (p+q) overflowed, ignore the overflow and increment (p+q)
if lo > M {
lo &= M;
lo += 1;
}
lo
}
fn os_random() -> u32 {
loop {
let seed = RAND_SEED.load(Ordering::Relaxed);
let rand = next_random(seed);
match RAND_SEED.compare_exchange(seed, rand, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => return rand,
Err(_) => continue, // Another thread updated seed, retry
}
}
}
thread_local! {
static XORSHIFT_STATE: Cell<[u32; 4]> = {
Cell::new([
os_random(), // X: seeded from global Park-Miller RNG
842502087, // Y: constant
0x8767, // Z: constant
273326509, // W: constant
])
};
}
pub fn generate_identity_hash() -> u32 {
XORSHIFT_STATE.with(|state| {
let mut s = state.get();
let mut t = s[0];
t ^= t << 11;
s[0] = s[1];
s[1] = s[2];
s[2] = s[3];
let mut v = s[3];
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8));
s[3] = v;
state.set(s);
v
})
}

34
src/thread.rs Normal file
View File

@ -0,0 +1,34 @@
use std::sync::{Arc, Mutex};
use crate::class_file::ClassFile;
use crate::class_loader::{ClassLoader, LoaderRef};
use crate::Frame;
use crate::vm::Vm;
// A thread of execution
pub struct VmThread {
vm: Arc<Vm>,
loader: Arc<Mutex<ClassLoader>>,
frame_stack: Vec<Frame>
}
impl VmThread {
pub fn new(vm: Arc<Vm>, loader: LoaderRef, ) -> Self {
Self {
vm,
loader,
frame_stack: Vec::new(),
}
}
pub fn get_or_resolve_class(&self, what: &str) -> () {
let class_file = self.loader.lock().unwrap().get_or_load(what).unwrap();
self.init(class_file)
}
fn init(&self, class: Arc<ClassFile>) {
class.methods.first()
}
}

27
src/vm.rs Normal file
View File

@ -0,0 +1,27 @@
use std::sync::{Arc, Mutex};
use crate::class_loader::ClassLoader;
use crate::Frame;
use crate::thread::VmThread;
// struct AbstractObject<'a> {}
pub struct Vm {
// for now, model just a single thread
pub thread: Vec<VmThread>,
pub loader: Arc<Mutex<ClassLoader>>
}
impl Vm {
pub fn new() -> Self {
Self {
loader: Arc::new(Mutex::from(ClassLoader::default())),
thread: Vec::new(),
}
}
pub fn get_or_resolve_class(&self, what: &str) -> () {
let class_file = self.loader.lock().unwrap().get_or_load(what).unwrap();
self.init(class_file)
}
fn init() -> () {}
}