diff --git a/.gitignore b/.gitignore index c3c6f30..e3af16d 100644 --- a/.gitignore +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0eb5201..46a9f8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } \ No newline at end of file +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" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..47ba13d --- /dev/null +++ b/build.rs @@ -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!"); +} \ No newline at end of file diff --git a/src/attributes.rs b/src/attributes.rs new file mode 100644 index 0000000..ee1a71c --- /dev/null +++ b/src/attributes.rs @@ -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, + #[deku(skip)] + pub interpreted: Option, +} + +#[derive(Clone, PartialEq, Debug)] +pub enum Attribute { + // "Critical" + ConstantValue, + Code(CodeAttribute), + StackMapTable(Vec), + BootstrapMethods, + NestHost, + NestMembers, + PermittedSubclasses, + + SourceFile(u16), + LineNumberTable(LineNumberTableAttribute), + Exceptions(Vec), + InnerClasses(Vec), + Signature(u16), + LocalVariableTable(LocalVariableTableAttribute), + Unknown(String, Vec), +} + +//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 for Ops { +// type Error = (); +// +// fn try_from(value: u8) -> Result { +// 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 { + 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 { + 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, + pub exception_table_length: u16, + #[deku(count = "exception_table_length")] + pub exception_table: Vec, + pub attributes_count: u16, + #[deku(count = "attributes_count")] + pub attributes: Vec, +} + +#[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, +} + +#[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, +} + + + +#[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() + ) + } +} \ No newline at end of file diff --git a/src/bimage.rs b/src/bimage.rs new file mode 100644 index 0000000..e0bedfb --- /dev/null +++ b/src/bimage.rs @@ -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, + modules: Vec, +} + +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::>().into_iter().collect::>(); + modules.sort(); + Self { + image: reader, + modules, + } + } +} + + +impl Bimage { + pub fn new(path: impl AsRef) -> 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 { + let path = Self::resolve_path(module, class); + self.image.read_file(&path).unwrap() + } +} \ No newline at end of file diff --git a/src/class.rs b/src/class.rs new file mode 100644 index 0000000..428190a --- /dev/null +++ b/src/class.rs @@ -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>, + pub access_flags: ClassFlags, + pub this_class: String, + pub super_class: Arc, + pub interfaces: Vec>, + pub fields: Vec, + pub methods: Vec, +} + +impl From 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![], + } + } +} \ No newline at end of file diff --git a/src/class_file.rs b/src/class_file.rs new file mode 100644 index 0000000..6104ffc --- /dev/null +++ b/src/class_file.rs @@ -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| -> Result<_, DekuError> { Ok(Arc::new(v)) }" + )] + pub constant_pool: Arc>, + pub access_flags: u16, + pub this_class: u16, + pub super_class: u16, + interfaces_count: u16, + #[deku(count = "interfaces_count")] + pub interfaces: Vec, + fields_count: u16, + #[deku(count = "fields_count")] + pub fields: Vec, + methods_count: u16, + #[deku(count = "methods_count")] + pub methods: Vec, + attributes_count: u16, + #[deku(count = "attributes_count")] + pub attributes: Vec, +} + +#[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, +} + +#[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, +} + +#[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 +} + +#[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> { + 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 { + 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 { + 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> { + 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, +} + +type CONSTANT_POOL = [CpInfo]; + +pub trait ConstantPoolExt { + fn get_constant(&self, index: u16) -> Option<&CpInfo>; + fn get_string(&self, index: u16) -> Result; + 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; + fn resolve_method_ref(&self, index: u16) -> Result; + fn resolve_method_info(&self, method: &MethodInfo) -> Result; + fn resolve_field_info(&self, field: &FieldInfo) -> Result; +} + +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 { + 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 { + 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 { + 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 { + 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 { + 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 +} + + + +#[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 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 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 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 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 { + 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, ()> { + 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, ()> { + 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 { + 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 { + 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(()), + }) + } +} \ No newline at end of file diff --git a/src/class_loader.rs b/src/class_loader.rs new file mode 100644 index 0000000..32a143c --- /dev/null +++ b/src/class_loader.rs @@ -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>; + +#[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` 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>, + bimage: Bimage, +} + +impl ClassLoader { + pub fn new() -> Result { + 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` 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)` - 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` 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 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, 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 { + self.classes.clone() + }*/ + + fn load_class(&mut self, what: &str) -> Result, 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 { +// 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) +// } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..64aedf1 --- /dev/null +++ b/src/lib.rs @@ -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>; +#[derive(Debug)] +#[derive(Clone)] +enum Value { + Boolean(bool), + Char(char), + Float(f32), + Double(f64), + Byte(i8), + Short(i16), + Int(i32), + Long(i64), + Reference(Option), +} + + +struct Frame { + + // program counter + pc: u16, + // operand stack + stack: Vec, + // local vars + vars: Vec, + // constant pool + pool: Arc>, + + bytecode: Bytecode, + + thread: Arc +} + +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>, mut locals: Vec, thread: Arc) -> 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 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, + // none = void/v + return_type: Option +} +#[derive(Debug, PartialEq)] +pub enum FieldType { + Base(BaseType), + // L ClassName + ClassType(String), + // [ ComponentType + ArrayType(Box), +} + +enum ExecutionResult { + Continue, + Return(()), + ReturnValue(Value) +} +impl Frame { + fn execute_instruction(&mut self, op: &Ops) -> Result { + 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 = 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) } + } + } +} \ No newline at end of file diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..a589f55 --- /dev/null +++ b/src/macros.rs @@ -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)}; +} diff --git a/src/main.rs b/src/main.rs index 8f7d295..55081ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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::(&*bytes).unwrap(); - println!("{:?}", cf); -} - - - -#[deku(magic="")] -pub struct ClassFile { - pub magic: u32, - pub minor_version: u16, - pub major_version: u16, - pub constant_pool: Vec, // Note: count is pool.len() + 1 - pub access_flags: u16, - pub this_class: u16, - pub super_class: u16, - pub interfaces: Vec, - pub fields: Vec, - pub methods: Vec, - pub attributes: Vec, -} - -// 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 + jvm_rs::run() } \ No newline at end of file diff --git a/src/object.rs b/src/object.rs new file mode 100644 index 0000000..92ece6e --- /dev/null +++ b/src/object.rs @@ -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, + fields: Arc>>, +} + +impl PartialEq for Object { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for Object {} \ No newline at end of file diff --git a/src/rng.rs b/src/rng.rs new file mode 100644 index 0000000..873387f --- /dev/null +++ b/src/rng.rs @@ -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 + }) +} \ No newline at end of file diff --git a/src/thread.rs b/src/thread.rs new file mode 100644 index 0000000..ecb71dd --- /dev/null +++ b/src/thread.rs @@ -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, + loader: Arc>, + frame_stack: Vec +} + +impl VmThread { + + pub fn new(vm: Arc, 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) { + class.methods.first() + } +} \ No newline at end of file diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..9d3882c --- /dev/null +++ b/src/vm.rs @@ -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, + pub loader: Arc> +} + +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() -> () {} +} \ No newline at end of file