checkpoint
This commit is contained in:
parent
6c5900e79a
commit
e39ae877a3
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||
18
Cargo.toml
18
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" }
|
||||
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
18
build.rs
Normal 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
624
src/attributes.rs
Normal 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
58
src/bimage.rs
Normal 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
38
src/class.rs
Normal 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
821
src/class_file.rs
Normal 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
317
src/class_loader.rs
Normal 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
373
src/lib.rs
Normal 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
42
src/macros.rs
Normal 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)};
|
||||
}
|
||||
83
src/main.rs
83
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::<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
19
src/object.rs
Normal 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
69
src/rng.rs
Normal 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
34
src/thread.rs
Normal 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
27
src/vm.rs
Normal 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() -> () {}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user