Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Serial-ATA committed Jan 13, 2025
1 parent 59784a4 commit b0cb046
Show file tree
Hide file tree
Showing 15 changed files with 687 additions and 179 deletions.
147 changes: 82 additions & 65 deletions runtime/src/classpath/classloader.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::{Result, RuntimeError};
use crate::modules::{Module, Package};
use crate::modules::{Module, ModuleLockGuard, Package};
use crate::objects::class::Class;
use crate::objects::reference::Reference;

Expand Down Expand Up @@ -29,9 +29,17 @@ pub struct ClassLoader {
obj: Reference,

unnamed_module: SyncUnsafeCell<Option<Module>>,

classes: Mutex<HashMap<Symbol, &'static Class>>,
modules: Mutex<HashMap<Symbol, &'static Module>>,
packages: Mutex<HashMap<Symbol, &'static Package>>,

// TODO: Is there a better way to do this?
// Keep the java.base module separate from the other modules for bootstrapping. This field is only
// valid for the bootstrap loader.
java_base: SyncUnsafeCell<Option<Module>>,

// Access to these fields is manually synchronized with the global module mutex
modules: SyncUnsafeCell<HashMap<Symbol, Module>>,
packages: SyncUnsafeCell<HashMap<Symbol, Package>>,
}

impl PartialEq for ClassLoader {
Expand All @@ -49,25 +57,35 @@ impl Debug for ClassLoader {
}
}

// Module locked methods
impl ClassLoader {
/// Stores a copy of the `jdk.internal.loader.BootLoader#UNNAMED_MODULE` field
///
/// This is called from `jdk.internal.loader.BootLoader#setBootLoaderUnnamedModule0`, and can
/// only be set once.
pub fn set_bootloader_unnamed_module(entry: Module) {
let bootloader = ClassLoader::bootstrap();
pub fn insert_package_if_absent(&self, _guard: &ModuleLockGuard, package: Package) {
let packages = unsafe { &mut *self.packages.get() };
packages.entry(package.name()).or_insert(package);
}

let ptr = bootloader.unnamed_module.get();
assert!(
unsafe { (*ptr).is_none() },
"Attempt to set unnamed module for bootloader twice"
);
pub fn lookup_package(&self, _guard: &ModuleLockGuard, name: Symbol) -> Option<&Package> {
let packages = unsafe { &*self.packages.get() };
packages.get(&name)
}

unsafe {
*ptr = Some(entry);
}
pub fn insert_module(&self, _guard: &ModuleLockGuard, module: Module) {
let Some(module_name) = module.name() else {
panic!("Attempted to insert an unnamed module using `insert_module`")
};

let modules = unsafe { &mut *self.modules.get() };
modules.entry(module_name).or_insert(module);
}

pub fn lookup_module(&self, _guard: &ModuleLockGuard, name: Symbol) -> Option<&Module> {
let modules = unsafe { &*self.modules.get() };
modules.get(&name)
}
}

// Bootstrap methods
impl ClassLoader {
pub fn bootstrap() -> &'static Self {
static BOOTSTRAP_LOADER: LazyLock<SyncUnsafeCell<ClassLoader>> = LazyLock::new(|| {
let loader = ClassLoader {
Expand All @@ -76,28 +94,69 @@ impl ClassLoader {

unnamed_module: SyncUnsafeCell::new(None),
classes: Mutex::new(HashMap::new()),
modules: Mutex::new(HashMap::new()),
packages: Mutex::new(HashMap::new()),

java_base: SyncUnsafeCell::new(None),
modules: SyncUnsafeCell::new(HashMap::new()),
packages: SyncUnsafeCell::new(HashMap::new()),
};

SyncUnsafeCell::new(loader)
});

unsafe { &*BOOTSTRAP_LOADER.get() }
}
}

impl ClassLoader {
pub fn java_base(&self) -> &Module {
assert!(self.is_bootstrap());
unsafe { &*self.java_base.get() }
.as_ref()
.expect("java.base should be set")
}

pub fn set_java_base(&self, java_base: Module) {
let ptr = self.java_base.get();
assert!(
unsafe { &*ptr }.is_none(),
"java.base cannot be set more than once"
);

unsafe { *ptr = Some(java_base) }
}

pub fn is_bootstrap(&self) -> bool {
self.flags.is_bootstrap
}

/// Stores a copy of the `jdk.internal.loader.BootLoader#UNNAMED_MODULE` field
///
/// This is called from `jdk.internal.loader.BootLoader#setBootLoaderUnnamedModule0`, and can
/// only be set once.
pub fn set_bootloader_unnamed_module(entry: Module) {
let bootloader = ClassLoader::bootstrap();

let ptr = bootloader.unnamed_module.get();
assert!(
unsafe { (*ptr).is_none() },
"Attempt to set unnamed module for bootloader twice"
);

unsafe {
*ptr = Some(entry);
}
}
}

impl ClassLoader {
pub(crate) fn lookup_class(&self, name: Symbol) -> Option<&'static Class> {
let loaded_classes = self.classes.lock().unwrap();
loaded_classes.get(&name).map(|&class| class)
}

pub fn unnamed_module(&self) -> &Module {
unsafe { &*self.unnamed_module.get() }
.as_ref()
.expect("unnamed module should be set")
}
}

impl ClassLoader {
Expand Down Expand Up @@ -301,37 +360,6 @@ impl ClassLoader {
array_class
}

/// Get the package name from a fully qualified class name
pub fn package_name_for_class(name: Symbol) -> Result<Option<&'static str>> {
let name_str = name.as_str();

if name_str.is_empty() {
return Err(RuntimeError::BadClassName);
}

let Some(end) = name_str.as_bytes().iter().rposition(|c| *c == b'/') else {
return Ok(None);
};

let mut start_index = 0;

// Skip over '['
if name_str.starts_with('[') {
start_index = name_str.chars().skip_while(|c| *c == '[').count();

// A fully qualified class name should not contain a 'L'
if start_index >= name_str.len() || name_str.as_bytes()[start_index] == b'L' {
return Err(RuntimeError::BadClassName);
}
}

if start_index >= name_str.len() {
return Err(RuntimeError::BadClassName);
}

return Ok(Some(&name_str[start_index..end]));
}

/// Recreate mirrors for all loaded classes
pub fn fixup_mirrors() {
let bootstrap_loader = ClassLoader::bootstrap();
Expand All @@ -344,11 +372,10 @@ impl ClassLoader {
}

/// Sets all currently loaded classes to be members of `java.base`
pub fn fixup_modules() {
pub fn fixup_modules(obj: Reference) {
let bootstrap_loader = ClassLoader::bootstrap();
let java_base = crate::modules::java_base();
for class in bootstrap_loader.classes.lock().unwrap().values() {
class.mirror().get().set_module(java_base.obj());
class.mirror().get().set_module(obj.clone());
}
}
}
Expand All @@ -369,14 +396,4 @@ fn init_mirror(class: &'static Class) {
unsafe {
class.set_mirror();
}

// Set the module
if !crate::modules::java_base().has_obj() {
// Assume we are early in VM initialization, where `java.base` isn't a real module yet.
// In this case, we can assume that the intended module is `java.base`.
class.mirror().get().set_module(Reference::null());
return;
}

todo!("Setting modules other than java.base");
}
8 changes: 3 additions & 5 deletions runtime/src/initialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::objects::reference::Reference;
use crate::string_interner::StringInterner;
use crate::thread::{JavaThread, JavaThreadBuilder};

use crate::modules::Module;
use classfile::accessflags::MethodAccessFlags;
use common::int_types::s4;
use instructions::Operand;
Expand Down Expand Up @@ -37,9 +38,9 @@ pub fn create_java_vm(args: Option<&JavaVMInitArgs>) -> JavaVm {
/// 3. *Initialize* some of the classes that were loaded.
/// 4. Create the initial `java.lang.Thread` for the current thread.
///
/// [`create_java_base()`]: crate::modules::create_java_base()
/// [`create_java_base()`]: crate::modules::ModuleLockGuard::create_java_base()
fn initialize_thread(thread: &JavaThread) {
crate::modules::create_java_base();
crate::modules::with_module_lock(|guard| guard.create_java_base());

// Load some important classes
load_global_classes();
Expand Down Expand Up @@ -92,9 +93,6 @@ fn load_global_classes() {
// Fixup mirrors, as we have classes that were loaded before java.lang.Class
ClassLoader::fixup_mirrors();

// Fixup modules, as they rely on class mirrors
ClassLoader::fixup_modules();

load!(
jdk_internal_misc_UnsafeConstants,
java_lang_System,
Expand Down
1 change: 1 addition & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#![feature(core_intrinsics)]
#![feature(try_with_capacity)]
#![feature(array_chunks)]
#![feature(try_trait_v2)]

pub mod calls;
pub mod classpath;
Expand Down
Loading

0 comments on commit b0cb046

Please sign in to comment.