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