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
|
# 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.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
.idea/
|
.idea/
|
||||||
|
data
|
||||||
|
lib
|
||||||
|
output
|
||||||
18
Cargo.toml
18
Cargo.toml
@ -4,9 +4,15 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
spacetimedb-sdk = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
|
deku = { version = "0.20.0", features = ["logging"] }
|
||||||
#spacetimedb-core = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
|
deku_derive = "0.20.0"
|
||||||
#spacetimedb-lib = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
|
log = "0.4.28"
|
||||||
#spacetimedb-sats = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
|
env_logger = "0.11.8"
|
||||||
#spacetimedb-client-api-messages = { git = "https://github.com/infernap12/SpacetimeDB", branch = "class_de" }
|
itertools = "0.14.0"
|
||||||
#bindings = { git = "https://github.com/infernap12/bitcraft-bindings", branch = "class_de" }
|
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() {
|
fn main() {
|
||||||
let mut class_file = File::open("./data/main.class").unwrap();
|
jvm_rs::run()
|
||||||
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>
|
|
||||||
}
|
}
|
||||||
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