From 8fe2509244c255e05236488e119846af5371c373 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 1 Jan 2025 01:37:58 -0500 Subject: [PATCH] wip --- generators/native_methods/src/definitions.rs | 33 ++- generators/native_methods/src/parse/method.rs | 4 + .../native_methods/src/registernatives.rs | 2 +- generators/native_methods/src/util.rs | 24 +- runtime/src/classpath/classloader.rs | 227 +++++++++++++----- runtime/src/globals/classes.rs | 1 + runtime/src/globals/fields.rs | 10 +- runtime/src/initialization.rs | 29 ++- runtime/src/lib.rs | 1 + runtime/src/modules/entry.rs | 78 ++++++ runtime/src/modules/mod.rs | 46 ++++ runtime/src/modules/package.rs | 33 +++ runtime/src/native/java/io/FileDescriptor.rs | 14 +- runtime/src/native/java/io/FileInputStream.rs | 5 +- .../src/native/java/io/FileOutputStream.rs | 5 +- runtime/src/native/java/io/UnixFileSystem.rs | 10 +- runtime/src/native/java/lang/Class.rs | 9 +- runtime/src/native/java/lang/ClassLoader.rs | 19 +- runtime/src/native/java/lang/Double.rs | 5 +- runtime/src/native/java/lang/Float.rs | 6 +- runtime/src/native/java/lang/StringUTF16.rs | 3 +- runtime/src/native/java/lang/System.rs | 40 ++- runtime/src/native/java/lang/Thread.rs | 40 ++- runtime/src/native/java/lang/Throwable.rs | 17 +- runtime/src/native/java/lang/ref/Finalizer.rs | 4 +- runtime/src/native/java/lang/ref/Reference.rs | 7 +- .../native/java/security/AccessController.rs | 10 +- .../native/jdk/internal/loader/BootLoader.rs | 17 +- .../jdk/internal/loader/NativeLibraries.rs | 4 + runtime/src/native/jdk/internal/misc/CDS.rs | 18 +- .../src/native/jdk/internal/misc/Signal.rs | 11 +- runtime/src/native/jdk/internal/misc/VM.rs | 19 +- .../native/jdk/internal/reflect/Reflection.rs | 10 +- .../native/jdk/internal/util/SystemProps.rs | 9 +- runtime/src/native/jni/class.rs | 13 +- runtime/src/native/lookup.rs | 23 +- runtime/src/native/mod.rs | 93 +++++-- runtime/src/objects/array.rs | 4 +- runtime/src/objects/class/mod.rs | 23 +- runtime/src/objects/constant_pool/cp_types.rs | 2 +- runtime/src/objects/method/mod.rs | 16 +- runtime/src/objects/mirror.rs | 81 +++++-- runtime/src/objects/mod.rs | 1 - runtime/src/objects/module.rs | 51 ---- runtime/src/thread/java_lang_Thread.rs | 3 - runtime/src/thread/mod.rs | 11 +- symbols/src/lib.rs | 5 + 47 files changed, 777 insertions(+), 319 deletions(-) create mode 100644 runtime/src/modules/entry.rs create mode 100644 runtime/src/modules/mod.rs create mode 100644 runtime/src/modules/package.rs delete mode 100644 runtime/src/objects/module.rs diff --git a/generators/native_methods/src/definitions.rs b/generators/native_methods/src/definitions.rs index d2a065f..6380cb2 100644 --- a/generators/native_methods/src/definitions.rs +++ b/generators/native_methods/src/definitions.rs @@ -49,19 +49,33 @@ pub fn generate_definitions_for_class(def_path: &Path, class: &Class) { writeln!(definitions_file, "}}").unwrap(); } +macro_rules! non_static_signature { + () => { + "\tpub fn _{}(env: std::ptr::NonNull<::jni::env::JniEnv>, locals: \ + crate::stack::local_stack::LocalStack) -> crate::native::NativeReturn {{" + }; +} + +macro_rules! static_signature { + () => { + "\tpub fn _{}(env: std::ptr::NonNull<::jni::env::JniEnv>, class: &'static \ + crate::objects::class::Class, locals: crate::stack::local_stack::LocalStack) -> \ + crate::native::NativeReturn {{" + }; +} +const STATIC_SIGNATURE: &str = "pub fn "; + fn generate_methods_for_class(class: &Class, definitions_file: &mut File) { for method in class.methods().filter(|method| { method.name.is_some() && method.modifiers.contains(AccessFlags::ACC_NATIVE) }) { - writeln!( - definitions_file, - "\tpub fn _{}(env: std::ptr::NonNull<::jni::env::JniEnv>, locals: \ - crate::stack::local_stack::LocalStack) -> crate::native::NativeReturn {{", - method.name() - ) - .unwrap(); + if method.is_static() { + writeln!(definitions_file, static_signature!(), method.name()).unwrap(); + } else { + writeln!(definitions_file, non_static_signature!(), method.name()).unwrap(); + } - let is_static = method.modifiers.contains(AccessFlags::ACC_STATIC); + let is_static = method.is_static(); if !is_static { writeln!( definitions_file, @@ -95,13 +109,12 @@ fn generate_methods_for_class(class: &Class, definitions_file: &mut File) { } } - // TODO: static methods should have a `class` argument let mut method_call = String::new(); write!( method_call, "super::{}(env,{}", method.name(), - if is_static { "" } else { "this," } + if is_static { "class," } else { "this," } ) .unwrap(); diff --git a/generators/native_methods/src/parse/method.rs b/generators/native_methods/src/parse/method.rs index 815f35b..ff62154 100644 --- a/generators/native_methods/src/parse/method.rs +++ b/generators/native_methods/src/parse/method.rs @@ -25,6 +25,10 @@ impl Method { } } + pub fn is_static(&self) -> bool { + self.modifiers.contains(AccessFlags::ACC_STATIC) + } + /// The symbol for the class name + method name /// /// For example, `java.lang.Object#hashCode` would become `Object_hashCode`. diff --git a/generators/native_methods/src/registernatives.rs b/generators/native_methods/src/registernatives.rs index e9f26e6..90b500a 100644 --- a/generators/native_methods/src/registernatives.rs +++ b/generators/native_methods/src/registernatives.rs @@ -12,7 +12,7 @@ macro_rules! native_method_table_file_header { static NATIVES_REGISTERED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); #[allow(trivial_casts, unused_imports)] -pub fn registerNatives(_: std::ptr::NonNull) {{ +pub fn registerNatives(_: std::ptr::NonNull, _: &'static crate::objects::class::Class) {{ use symbols::sym; if NATIVES_REGISTERED.compare_exchange(false, true, std::sync::atomic::Ordering::SeqCst, std::sync::atomic::Ordering::Acquire) != Ok(false) {{ diff --git a/generators/native_methods/src/util.rs b/generators/native_methods/src/util.rs index 1165cc7..c2851e7 100644 --- a/generators/native_methods/src/util.rs +++ b/generators/native_methods/src/util.rs @@ -6,17 +6,31 @@ use std::path::{Component, Path}; /// Create a `NativeMethodDef` for a method pub(crate) fn method_table_entry(module: &str, class: &Class, method: &Method) -> String { + let ptr = if method.is_static() { + format!( + "NativeMethodPtr::new_static(crate::native::{}{}::definitions::_{})", + escape_module_name(module).replace('/', "::"), + class.class_name.replace('$', "::"), + method.name() + ) + } else { + format!( + "NativeMethodPtr::new_non_static(crate::native::{}{}::definitions::_{})", + escape_module_name(module).replace('/', "::"), + class.class_name.replace('$', "::"), + method.name() + ) + }; + format!( - "NativeMethodDef {{ class: sym!({}), name: sym!({}), descriptor: sym!({}) }}, \ - crate::native::{}{}::definitions::_{} as NativeMethodPtr", + "NativeMethodDef {{ class: sym!({}), name: sym!({}), descriptor: sym!({}), is_static: {} \ + }}, {ptr}", format!("{}{}", module, class.class_name) .replace('/', "_") .replace('$', "_"), method.name_symbol(), method.signature_symbol_name(), - escape_module_name(module).replace('/', "::"), - class.class_name.replace('$', "::"), - method.name() + method.is_static(), ) } diff --git a/runtime/src/classpath/classloader.rs b/runtime/src/classpath/classloader.rs index bd1022f..236d77b 100644 --- a/runtime/src/classpath/classloader.rs +++ b/runtime/src/classpath/classloader.rs @@ -1,12 +1,17 @@ use crate::error::{Result, RuntimeError}; +use crate::modules::{Module, Package}; use crate::objects::class::Class; +use crate::objects::reference::Reference; +use std::cell::SyncUnsafeCell; use std::collections::HashMap; +use std::fmt::Debug; use std::ops::RangeInclusive; use std::sync::{LazyLock, Mutex}; use classfile::{ClassFile, FieldType}; use common::int_types::u1; +use common::traits::PtrType; use symbols::{sym, Symbol}; const SUPPORTED_MAJOR_LOWER_BOUND: u1 = 45; @@ -14,48 +19,112 @@ const SUPPORTED_MAJOR_UPPER_BOUND: u1 = 67; const SUPPORTED_MAJOR_VERSION_RANGE: RangeInclusive = SUPPORTED_MAJOR_LOWER_BOUND..=SUPPORTED_MAJOR_UPPER_BOUND; -static BOOTSTRAP_LOADED_CLASSES: LazyLock>> = - LazyLock::new(|| Mutex::new(HashMap::new())); +#[derive(Copy, Clone, Debug)] +struct ClassLoaderFlags { + is_bootstrap: bool, +} + +pub struct ClassLoader { + flags: ClassLoaderFlags, + obj: Reference, + + unnamed_module: SyncUnsafeCell>, + classes: Mutex>, + modules: Mutex>, + packages: Mutex>, +} + +impl PartialEq for ClassLoader { + fn eq(&self, other: &Self) -> bool { + self.obj == other.obj + } +} -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum ClassLoader { - Bootstrap, - UserDefined, +impl Debug for ClassLoader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ClassLoader") + .field("flags", &self.flags) + .field("obj", &self.obj) + .finish() + } } impl ClassLoader { - pub(crate) fn lookup_class(name: Symbol) -> Option<&'static Class> { - let loaded_classes = BOOTSTRAP_LOADED_CLASSES.lock().unwrap(); + /// 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" + ); - loaded_classes.get(&name).map(|&class| class) + unsafe { + *ptr = Some(entry); + } + } + + pub fn bootstrap() -> &'static Self { + static BOOTSTRAP_LOADER: LazyLock> = LazyLock::new(|| { + let loader = ClassLoader { + flags: ClassLoaderFlags { is_bootstrap: true }, + obj: Reference::null(), + + unnamed_module: SyncUnsafeCell::new(None), + classes: Mutex::new(HashMap::new()), + modules: Mutex::new(HashMap::new()), + packages: Mutex::new(HashMap::new()), + }; + + SyncUnsafeCell::new(loader) + }); + + unsafe { &*BOOTSTRAP_LOADER.get() } } +} - fn insert_bootstrapped_class(name: Symbol, classref: &'static Class) { - let mut loaded_classes = BOOTSTRAP_LOADED_CLASSES.lock().unwrap(); - loaded_classes.insert(name, classref); +impl ClassLoader { + pub fn is_bootstrap(&self) -> bool { + self.flags.is_bootstrap } } impl ClassLoader { - pub fn load(&self, name: Symbol) -> Option<&'static Class> { - match self { - ClassLoader::Bootstrap => Self::load_bootstrap(name), - ClassLoader::UserDefined => unimplemented!("User defined loader"), + 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) + } +} + +impl ClassLoader { + pub fn obj(&self) -> Reference { + self.obj.clone() + } + + pub fn load(&'static self, name: Symbol) -> Option<&'static Class> { + if self.flags.is_bootstrap { + return self.load_bootstrap(name); } + + unimplemented!("User-defined class loaders") } // https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html#jvms-5.3.1 - fn load_bootstrap(name: Symbol) -> Option<&'static Class> { + fn load_bootstrap(&'static self, name: Symbol) -> Option<&'static Class> { // First, the Java Virtual Machine determines whether the bootstrap class loader has // already been recorded as an initiating loader of a class or interface denoted by N. // If so, this class or interface is C, and no class loading or creation is necessary. - if let ret @ Some(_) = Self::lookup_class(name) { + if let ret @ Some(_) = self.lookup_class(name) { return ret; } // Otherwise, the Java Virtual Machine passes the argument N to an invocation of a method on // the bootstrap class loader [...] and then [...] create C, via the algorithm of §5.3.5. - let classref = ClassLoader::Bootstrap.load_class_by_name(name); + let classref = self.load_class_by_name(name); // TODO: // If no purported representation of C is found, the bootstrap class loader throws a ClassNotFoundException. @@ -70,8 +139,8 @@ impl ClassLoader { // Deriving a Class from a class File Representation // https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html#jvms-5.3.5 - fn load_class_by_name(self, name: Symbol) -> &'static Class { - if let Some(class) = Self::lookup_class(name) { + fn load_class_by_name(&'static self, name: Symbol) -> &'static Class { + if let Some(class) = self.lookup_class(name) { return class; } @@ -87,14 +156,14 @@ impl ClassLoader { // 2. Otherwise, the Java Virtual Machine attempts to parse the purported representation. let classfile_bytes = super::find_classpath_entry(name); - let classfile = ClassFile::read_from(&mut &classfile_bytes[..]).unwrap(); // TODO: handle errors // The purported representation may not in fact be a valid representation of C, so // derivation must detect the following problems: - - // TODO: - // 2.1. If the purported representation is not a ClassFile structure (§4.1, §4.8), derivation - // throws a ClassFormatError. + let Ok(classfile) = ClassFile::read_from(&mut &classfile_bytes[..]) else { + // 2.1. If the purported representation is not a ClassFile structure (§4.1, §4.8), derivation + // throws a ClassFormatError. + panic!("ClassFormatError") // TODO + }; // 2.2. Otherwise, if the purported representation is not of a supported major or // minor version (§4.1), derivation throws an UnsupportedClassVersionError. @@ -103,18 +172,20 @@ impl ClassLoader { "UnsupportedClassVersionError" ); - // TODO: // 2.3. Otherwise, if the purported representation does not actually represent a class or // interface named N, derivation throws a NoClassDefFoundError. This occurs when the // purported representation has either a this_class item which specifies a name other // than N, or an access_flags item which has the ACC_MODULE flag set. + let specified_class_name = classfile.constant_pool.get_class_name(classfile.this_class); + if name_str.as_bytes() != specified_class_name || classfile.access_flags.is_module() { + panic!("NoClassDefFoundError") // TODO + } // 3. If C has a direct superclass, the symbolic reference from C to its direct // superclass is resolved using the algorithm of §5.4.3.1. Note that if C is an interface // it must have Object as its direct superclass, which must already have been loaded. // Only Object has no direct superclass. let mut super_class = None; - if let Some(super_class_name) = classfile.get_super_class() { super_class = Some(self.resolve_super_class(Symbol::intern_bytes(super_class_name))); } @@ -133,20 +204,13 @@ impl ClassLoader { // "Preparation may occur at any time following creation but must be completed prior to initialization." class.prepare(); - // Set the mirror if `java.lang.Class` is loaded - if Self::lookup_class(sym!(java_lang_Class)).is_some() { - // SAFETY: The only condition of `set_mirror` is that the class isn't in use yet. - unsafe { - class.set_mirror(); - } - } - - Self::insert_bootstrapped_class(name, class); + init_mirror(class); + self.classes.lock().unwrap().insert(name, class); class } - fn resolve_super_class(self, super_class_name: Symbol) -> &'static Class { + fn resolve_super_class(&'static self, super_class_name: Symbol) -> &'static Class { // Any exception that can be thrown as a result of failure of class or interface resolution // can be thrown as a result of derivation. In addition, derivation must detect the following problems: @@ -179,13 +243,13 @@ impl ClassLoader { // Creating array classes // https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html#jvms-5.3.3 - fn create_array_class(self, descriptor: Symbol) -> &'static Class { + fn create_array_class(&'static self, descriptor: Symbol) -> &'static Class { // The following steps are used to create the array class C denoted by the name N in association with the class loader L. // L may be either the bootstrap class loader or a user-defined class loader. // First, the Java Virtual Machine determines whether L has already been recorded as an initiating loader of an array class with // the same component type as N. If so, this class is C, and no array class creation is necessary. - if let Some(ret) = Self::lookup_class(descriptor) { + if let Some(ret) = self.lookup_class(descriptor) { return ret; } @@ -231,28 +295,12 @@ impl ClassLoader { // If the component type is a reference type, the accessibility of the array class is determined by the accessibility of its component type (§5.4.4). // Otherwise, the array class is accessible to all classes and interfaces. - // Set the mirror if `java.lang.Class` is loaded - if Self::lookup_class(sym!(java_lang_Class)).is_some() { - // SAFETY: The only condition of `set_mirror` is that the class isn't in use yet. - unsafe { - array_class.set_mirror(); - } - } + init_mirror(array_class); - Self::insert_bootstrapped_class(descriptor, array_class); + self.classes.lock().unwrap().insert(descriptor, array_class); array_class } - /// Recreate mirrors for all loaded classes - pub fn fixup_mirrors() { - for (_, class) in BOOTSTRAP_LOADED_CLASSES.lock().unwrap().iter() { - // SAFETY: The only condition of `set_mirror` is that the class isn't in use yet. - unsafe { - class.set_mirror(); - } - } - } - /// Get the package name from a fully qualified class name pub fn package_name_for_class(name: Symbol) -> Result> { let name_str = name.as_str(); @@ -283,4 +331,67 @@ impl ClassLoader { return Ok(Some(&name_str[start_index..end])); } + + /// Recreate mirrors for all loaded classes + pub fn fixup_mirrors() { + let bootstrap_loader = ClassLoader::bootstrap(); + for class in bootstrap_loader.classes.lock().unwrap().values() { + // SAFETY: The only condition of `set_mirror` is that the class isn't in use yet. + unsafe { + class.set_mirror(); + } + } + } + + /// Sets all currently loaded classes to be members of `java.base` + pub fn fixup_modules() { + 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()); + } + } +} + +fn init_mirror(class: &'static Class) { + let bootstrap_loader = ClassLoader::bootstrap(); + + // Set the mirror if `java.lang.Class` is loaded + let class_loaded = bootstrap_loader + .lookup_class(sym!(java_lang_Class)) + .is_some(); + if !class_loaded { + // We cannot do anything to this class until a mirror is available. + return; + } + + // SAFETY: The only condition of `set_mirror` is that the class isn't in use yet. + unsafe { + class.set_mirror(); + } + + // Set the module + let module; + let java_base = crate::modules::java_base(); + if !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`. + module = java_base; + } else { + todo!() + } + + if !module.has_obj() { + assert_eq!( + module.name(), + Some(sym!(java_base)), + "only java.base can have no associated object" + ); + + // `java.base` isn't a real module yet. Will need to fix this up later. + class.mirror().get().set_module(Reference::null()); + return; + } + + class.mirror().get().set_module(module.obj()); } diff --git a/runtime/src/globals/classes.rs b/runtime/src/globals/classes.rs index d481d76..7e7a608 100644 --- a/runtime/src/globals/classes.rs +++ b/runtime/src/globals/classes.rs @@ -48,6 +48,7 @@ define_classes!( java_lang_StackTraceElement, java_lang_Throwable, java_lang_Cloneable, + java_io_Serializable, java_lang_Module, java_lang_ref_Reference, java_lang_ref_Finalizer, diff --git a/runtime/src/globals/fields.rs b/runtime/src/globals/fields.rs index 67caf2e..228c8f6 100644 --- a/runtime/src/globals/fields.rs +++ b/runtime/src/globals/fields.rs @@ -96,6 +96,14 @@ pub mod java_lang_Class { /// /// Expected type: `Reference` to `java.lang.String` @FIELD name: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/String"), + /// `java.lang.Class#module` field offset + /// + /// Expected type: `Reference` to `java.lang.Module` + @FIELD module: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/Module"), + /// `java.lang.Class#classLoader` field offset + /// + /// Expected type: `Reference` to `java.lang.ClassLoader` + @FIELD classLoader: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/ClassLoader"), } } @@ -221,7 +229,7 @@ pub mod java_lang_Throwable { /// `java.lang.Throwable#backtrace` field offset /// /// Expected field type: `Reference` to `java.lang.Object` - @FIELD backtrace: ty @ FieldType::Object(_), + @FIELD backtrace: FieldType::Object(_), /// `java.lang.Throwable#depth` field offset /// /// Expected field type: `jint` diff --git a/runtime/src/initialization.rs b/runtime/src/initialization.rs index f494dbf..c5931a9 100644 --- a/runtime/src/initialization.rs +++ b/runtime/src/initialization.rs @@ -4,10 +4,9 @@ use crate::native::jni::invocation_api::main_java_vm; use crate::objects::class_instance::ClassInstance; use crate::objects::reference::Reference; use crate::string_interner::StringInterner; -use crate::thread::{java_lang_Thread, JavaThread, JavaThreadBuilder}; +use crate::thread::{JavaThread, JavaThreadBuilder}; use classfile::accessflags::MethodAccessFlags; -use classfile::FieldType; use common::int_types::s4; use instructions::Operand; use jni::java_vm::JavaVm; @@ -24,10 +23,26 @@ pub fn create_java_vm(args: Option<&JavaVMInitArgs>) -> JavaVm { unsafe { main_java_vm() } } +/// The entire initialization stage of the VM +/// +/// The bulk of initialization is handled in the `java.lang.System#initPhase{1,2,3}` methods, but there +/// is some work we need to do before that point. +/// +/// The order of operations is very important: +/// +/// 1. Create a dummy `java.base` module +/// * This is needed for class loading. The module is fixed up later when `java.lang.Module` is +/// initialized. For now, it will be in an invalid state. See [`create_java_base()`]. +/// 2. Load important classes & store their field offsets. +/// 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() fn initialize_thread(thread: &JavaThread) { - // Load some important classes first - load_global_classes(); + crate::modules::create_java_base(); + // Load some important classes + load_global_classes(); init_field_offsets(); // Init some important classes @@ -60,7 +75,7 @@ fn load_global_classes() { ($($name:ident),+ $(,)?) => {{ paste::paste! { $( - let class = ClassLoader::Bootstrap.load(sym!($name)).unwrap(); + let class = ClassLoader::bootstrap().load(sym!($name)).unwrap(); unsafe { $crate::globals::classes::[](class); } )+ } @@ -77,6 +92,9 @@ 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, @@ -86,6 +104,7 @@ fn load_global_classes() { java_lang_ThreadGroup, java_lang_Throwable, java_lang_Cloneable, + java_io_Serializable, java_lang_ref_Reference, java_lang_ref_Finalizer, jdk_internal_reflect_MethodAccessorImpl, diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 3a107a8..9ed18b0 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -15,6 +15,7 @@ pub mod globals; mod initialization; mod interpreter; mod method_invoker; +pub mod modules; pub mod native; pub mod objects; pub mod stack; diff --git a/runtime/src/modules/entry.rs b/runtime/src/modules/entry.rs new file mode 100644 index 0000000..9033ee5 --- /dev/null +++ b/runtime/src/modules/entry.rs @@ -0,0 +1,78 @@ +use crate::objects::reference::Reference; + +use symbols::Symbol; + +pub struct Module { + pub(super) obj: Reference, + pub(super) open: bool, + pub(super) name: Option, + pub(super) version: Option, + pub(super) location: Option, +} + +impl Module { + /// Create an unnamed `Module` + /// + /// Every `java.lang.ClassLoader` has an `UNNAMED_MODULE` field, which holds a `java.lang.Module` + /// with no name. This module contains any types loaded by the `ClassLoader` that do not belong + /// to any module. + /// + /// There are special rules for unnamed modules, designed to maximize their interoperation with + /// other run-time modules, as follows: + /// + /// * A class loader's unnamed module is distinct from all other run-time modules bound to the same class loader. + /// * A class loader's unnamed module is distinct from all run-time modules (including unnamed modules) bound to other class loaders. + /// * Every unnamed module reads every run-time module. + /// * Every unnamed module exports, to every run-time module, every run-time package associated with itself. + pub fn unnamed(obj: Reference) -> Self { + assert!(!obj.is_null()); + + Self { + obj, + open: true, + name: None, + version: None, + location: None, + } + } + + pub fn named( + name: Symbol, + obj: Reference, + version: Option, + location: Option, + ) -> Self { + // TODO: Assert is instance of java.lang.Module? + assert!(!obj.is_null()); + + Self { + obj, + open: false, + name: Some(name), + version, + location, + } + } +} + +impl Module { + /// Get the name of this module + /// + /// This will only return `None` for modules created with [`Module::unnamed()`] + pub fn name(&self) -> Option { + self.name + } + + /// Get the associated `java.lang.Module` instance + pub fn obj(&self) -> Reference { + self.obj.clone() + } + + /// Check whether this entry has an associated `java.lang.Module` object + /// + /// This is only needed for `java.base` early in VM initialization. It is always `true` for other + /// entries. + pub fn has_obj(&self) -> bool { + !self.obj.is_null() + } +} diff --git a/runtime/src/modules/mod.rs b/runtime/src/modules/mod.rs new file mode 100644 index 0000000..549c4e7 --- /dev/null +++ b/runtime/src/modules/mod.rs @@ -0,0 +1,46 @@ +use crate::objects::reference::Reference; + +use std::cell::SyncUnsafeCell; + +use symbols::sym; + +mod entry; + +pub use entry::Module; + +mod package; +pub use package::Package; + +/// Special case for `java.base`, as it is heavily used during VM initialization, and is initially +/// created in an **invalid state**. +static JAVA_BASE: SyncUnsafeCell> = SyncUnsafeCell::new(None); + +fn set_java_base(entry: Module) { + let ptr = JAVA_BASE.get(); + assert!(unsafe { &*ptr }.is_none(), "java.base can only be set once"); + unsafe { + *ptr = Some(entry); + } +} + +pub fn java_base() -> &'static Module { + let opt = unsafe { &*JAVA_BASE.get() }; + opt.as_ref().expect("java.base should be set") +} + +/// Create the entry for `java.base` +/// +/// This is only useful for bootstrapping purposes, very early in VM initialization. The [`Module`] +/// produced is **not valid**. +pub fn create_java_base() { + // Don't use the constructors, since they do validation. + let java_base_module = Module { + name: Some(sym!(java_base)), + open: true, + obj: Reference::null(), + version: None, + location: None, + }; + + set_java_base(java_base_module); +} diff --git a/runtime/src/modules/package.rs b/runtime/src/modules/package.rs new file mode 100644 index 0000000..a5dc856 --- /dev/null +++ b/runtime/src/modules/package.rs @@ -0,0 +1,33 @@ +use super::entry::Module; + +use symbols::Symbol; + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub enum PackageExportType { + /// Package is not exported + None, + /// Package is unqualifiedly exported + Unqualified, + /// Package is qualifiedly exported + AllUnnamed, + /// Package is exported to all unnamed modules + UnqualifiedOrAllUnnamed, +} + +/// A representation of a package in Java +pub struct Package { + name: Symbol, + module: &'static Module, + + export_type: PackageExportType, +} + +impl Package { + pub fn name(&self) -> Symbol { + self.name + } + + pub fn module(&self) -> &'static Module { + self.module + } +} diff --git a/runtime/src/native/java/io/FileDescriptor.rs b/runtime/src/native/java/io/FileDescriptor.rs index a232fde..cfac0e0 100644 --- a/runtime/src/native/java/io/FileDescriptor.rs +++ b/runtime/src/native/java/io/FileDescriptor.rs @@ -1,7 +1,7 @@ #![allow(non_upper_case_globals)] -use crate::classpath::classloader::ClassLoader; use crate::native::jni::IntoJni; +use crate::objects::class::Class; use crate::objects::instance::Instance; use crate::objects::reference::Reference; @@ -12,7 +12,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use ::jni::env::JniEnv; use ::jni::sys::{jboolean, jfieldID, jint, jlong}; use common::sync::ForceSync; -use symbols::sym; include_generated!("native/java/io/def/FileDescriptor.definitions.rs"); @@ -40,7 +39,7 @@ pub fn sync0(_: NonNull, _this: Reference) { } // TODO: Move logic to globals -pub fn initIDs(_: NonNull) { +pub fn initIDs(_: NonNull, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) @@ -50,7 +49,6 @@ pub fn initIDs(_: NonNull) { panic!("java.io.FileDescriptor#initIDs: attempt to initialize more than once."); } - let class = ClassLoader::lookup_class(sym!(java_io_FileDescriptor)).unwrap(); unsafe { crate::globals::classes::set_java_io_FileDescriptor(class); } @@ -90,23 +88,23 @@ pub fn initIDs(_: NonNull) { } #[cfg(windows)] -pub fn getHandle(_: NonNull, _d: jint) -> jlong { +pub fn getHandle(_: NonNull, _class: &'static Class, _d: jint) -> jlong { unimplemented!("java.io.FileDescriptor#getHandle"); } // Only windows uses the `handle` field. #[cfg(unix)] -pub fn getHandle(_: NonNull, _d: jint) -> jlong { +pub fn getHandle(_: NonNull, _class: &'static Class, _d: jint) -> jlong { -1 } #[cfg(windows)] -pub fn getAppend(_: NonNull, _fd: jint) -> jboolean { +pub fn getAppend(_: NonNull, _class: &'static Class, _fd: jint) -> jboolean { unimplemented!("java.io.FileDescriptor#getAppend"); } #[cfg(unix)] -pub fn getAppend(_: NonNull, fd_: jint) -> jboolean { +pub fn getAppend(_: NonNull, _class: &'static Class, fd_: jint) -> jboolean { use libc::{F_GETFL, O_APPEND}; let flags = unsafe { libc::fcntl(fd_, F_GETFL) }; diff --git a/runtime/src/native/java/io/FileInputStream.rs b/runtime/src/native/java/io/FileInputStream.rs index ccff61a..964bbf8 100644 --- a/runtime/src/native/java/io/FileInputStream.rs +++ b/runtime/src/native/java/io/FileInputStream.rs @@ -1,7 +1,7 @@ #![allow(non_upper_case_globals)] -use crate::classpath::classloader::ClassLoader; use crate::native::jni::IntoJni; +use crate::objects::class::Class; use crate::objects::reference::Reference; use std::cell::SyncUnsafeCell; @@ -69,7 +69,7 @@ pub fn isRegularFile0( } // TODO: Move logic to globals -pub fn initIDs(_: NonNull) { +pub fn initIDs(_: NonNull, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) @@ -79,7 +79,6 @@ pub fn initIDs(_: NonNull) { panic!("java.io.FileInputStream#initIDs: attempt to initialize more than once."); } - let class = ClassLoader::lookup_class(sym!(java_io_FileInputStream)).unwrap(); unsafe { crate::globals::classes::set_java_io_FileInputStream(class); } diff --git a/runtime/src/native/java/io/FileOutputStream.rs b/runtime/src/native/java/io/FileOutputStream.rs index 43f59d1..c7da402 100644 --- a/runtime/src/native/java/io/FileOutputStream.rs +++ b/runtime/src/native/java/io/FileOutputStream.rs @@ -1,8 +1,8 @@ #![allow(non_upper_case_globals)] -use crate::classpath::classloader::ClassLoader; use crate::native::jni::{field_ref_from_jfieldid, IntoJni}; use crate::native::Reference; +use crate::objects::class::Class; use crate::objects::instance::Instance; use crate::thread::JavaThread; @@ -99,7 +99,7 @@ pub fn writeBytes( } // TODO: Move logic to globals -pub fn initIDs(_: NonNull) { +pub fn initIDs(_: NonNull, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) @@ -109,7 +109,6 @@ pub fn initIDs(_: NonNull) { panic!("java.io.FileOutputStream#initIDs: attempt to initialize more than once."); } - let class = ClassLoader::lookup_class(sym!(java_io_FileOutputStream)).unwrap(); unsafe { crate::globals::classes::set_java_io_FileOutputStream(class); } diff --git a/runtime/src/native/java/io/UnixFileSystem.rs b/runtime/src/native/java/io/UnixFileSystem.rs index e119312..da60a41 100644 --- a/runtime/src/native/java/io/UnixFileSystem.rs +++ b/runtime/src/native/java/io/UnixFileSystem.rs @@ -1,7 +1,7 @@ #![allow(non_upper_case_globals)] -use crate::classpath::classloader::ClassLoader; use crate::native::jni::{field_ref_from_jfieldid, IntoJni}; +use crate::objects::class::Class; use crate::objects::instance::Instance; use crate::objects::reference::Reference; use crate::string_interner::StringInterner; @@ -203,7 +203,7 @@ pub fn getNameMax0( } // TODO: Move logic to globals -pub fn initIDs(_: NonNull) { +pub fn initIDs(_: NonNull, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) @@ -213,13 +213,13 @@ pub fn initIDs(_: NonNull) { panic!("java.io.UnixFileSystem#initIDs: attempt to initialize more than once."); } - let class = ClassLoader::lookup_class(sym!(java_io_File)).unwrap(); + let file_class = class.loader().load(sym!(java_io_File)).unwrap(); unsafe { - crate::globals::classes::set_java_io_File(class); + crate::globals::classes::set_java_io_File(file_class); } let mut field_set = false; - for field in class.fields() { + for field in file_class.fields() { if field.name == sym!(path) { unsafe { *java_io_file_path_field.get() = ForceSync::new(field.into_jni()); diff --git a/runtime/src/native/java/lang/Class.rs b/runtime/src/native/java/lang/Class.rs index 05ec476..6beeb49 100644 --- a/runtime/src/native/java/lang/Class.rs +++ b/runtime/src/native/java/lang/Class.rs @@ -1,6 +1,7 @@ use crate::include_generated; use crate::native::jni::{safe_classref_from_jclass, IntoJni}; use crate::native::JniEnv; +use crate::objects::class::Class; use crate::objects::instance::Instance; use crate::objects::mirror::MirrorInstance; use crate::objects::reference::Reference; @@ -21,6 +22,7 @@ include_generated!("native/java/lang/def/Class.definitions.rs"); // throws ClassNotFoundException pub fn forName0( _env: NonNull, + _class: &'static Class, _name: Reference, // java.lang.String _initialize: jboolean, _loader: Reference, // java.lang.ClassLoader @@ -144,7 +146,11 @@ pub fn getProtectionDomain0( { unimplemented!("Class#getProtectionDomain0"); } -pub fn getPrimitiveClass(_env: NonNull, name: Reference /* String */) -> Reference /* Class */ +pub fn getPrimitiveClass( + _env: NonNull, + _class: &'static Class, + name: Reference, // String +) -> Reference /* Class */ { let string_class = name.extract_class(); let name_string = StringInterner::rust_string_from_java_string(string_class); @@ -229,6 +235,7 @@ pub fn isRecord0(_env: NonNull, _this: Reference /* java.lang.Class */) #[allow(clippy::unnecessary_wraps, clippy::no_effect_underscore_binding)] pub fn desiredAssertionStatus0( _env: NonNull, + _class: &'static Class, clazz: Reference, // java/lang/Class ) -> jboolean { let mirror = clazz.extract_mirror(); diff --git a/runtime/src/native/java/lang/ClassLoader.rs b/runtime/src/native/java/lang/ClassLoader.rs index a37f6dd..6e41fac 100644 --- a/runtime/src/native/java/lang/ClassLoader.rs +++ b/runtime/src/native/java/lang/ClassLoader.rs @@ -1,4 +1,5 @@ use crate::native::JniEnv; +use crate::objects::class::Class; use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -10,6 +11,7 @@ include_generated!("native/java/lang/def/ClassLoader.definitions.rs"); pub fn defineClass1( _env: NonNull, + _class: &'static Class, _loader: Reference, // java.lang.ClassLoader _name: Reference, // java.lang.String _b: Reference, // byte[], @@ -19,11 +21,12 @@ pub fn defineClass1( _source: Reference, // java.lang.String ) -> Reference // java.lang.Class { - unimplemented!("java.lang.Class#defineClass1") + unimplemented!("java.lang.ClassLoader#defineClass1") } pub fn defineClass2( _env: NonNull, + _class: &'static Class, _loader: Reference, // java.lang.ClassLoader _name: Reference, // java.lang.String _b: Reference, // java.nio.ByteBuffer, @@ -33,11 +36,12 @@ pub fn defineClass2( _source: Reference, // java.lang.String ) -> Reference // java.lang.Class { - unimplemented!("java.lang.Class#defineClass2") + unimplemented!("java.lang.ClassLoader#defineClass2") } pub fn defineClass0( _env: NonNull, + _class: &'static Class, _loader: Reference, // java.lang.ClassLoader _lookup: Reference, // java.lang.Class _name: Reference, // java.lang.String @@ -50,15 +54,16 @@ pub fn defineClass0( _source: Reference, // java.lang.String ) -> Reference // java.lang.Class { - unimplemented!("java.lang.Class#defineClass0") + unimplemented!("java.lang.ClassLoader#defineClass0") } pub fn findBootstrapClass( _env: NonNull, + _class: &'static Class, _name: Reference, // java.lang.String ) -> Reference // java.lang.Class { - unimplemented!("java.lang.Class#findBootstrapClass") + unimplemented!("java.lang.ClassLoader#findBootstrapClass") } pub fn findLoadedClass0( @@ -67,10 +72,10 @@ pub fn findLoadedClass0( _name: Reference, // java.lang.String ) -> Reference // java.lang.Class { - unimplemented!("java.lang.Class#findLoadedClass0") + unimplemented!("java.lang.ClassLoader#findLoadedClass0") } -pub fn retrieveDirectives(_env: NonNull) -> Reference // AssertionStatusDirectives +pub fn retrieveDirectives(_env: NonNull, _class: &'static Class) -> Reference // AssertionStatusDirectives { - unimplemented!("java.lang.Class#retrieveDirectives") + unimplemented!("java.lang.ClassLoader#retrieveDirectives") } diff --git a/runtime/src/native/java/lang/Double.rs b/runtime/src/native/java/lang/Double.rs index 2afad79..453f479 100644 --- a/runtime/src/native/java/lang/Double.rs +++ b/runtime/src/native/java/lang/Double.rs @@ -1,4 +1,5 @@ use crate::native::JniEnv; +use crate::objects::class::Class; use std::ptr::NonNull; @@ -6,10 +7,10 @@ use common::int_types::{s8, u8}; include_generated!("native/java/lang/def/Double.definitions.rs"); -pub fn doubleToRawLongBits(_env: NonNull, value: f64) -> s8 { +pub fn doubleToRawLongBits(_env: NonNull, _class: &'static Class, value: f64) -> s8 { value.to_bits() as s8 } -pub fn longBitsToDouble(_env: NonNull, bits: s8) -> f64 { +pub fn longBitsToDouble(_env: NonNull, _class: &'static Class, bits: s8) -> f64 { f64::from_bits(bits as u8) } diff --git a/runtime/src/native/java/lang/Float.rs b/runtime/src/native/java/lang/Float.rs index b63720b..1fa7bee 100644 --- a/runtime/src/native/java/lang/Float.rs +++ b/runtime/src/native/java/lang/Float.rs @@ -1,3 +1,5 @@ +use crate::objects::class::Class; + use std::ptr::NonNull; use ::jni::env::JniEnv; @@ -5,10 +7,10 @@ use common::int_types::{s4, u4}; include_generated!("native/java/lang/def/Float.definitions.rs"); -pub fn floatToRawIntBits(_env: NonNull, value: f32) -> s4 { +pub fn floatToRawIntBits(_env: NonNull, _class: &'static Class, value: f32) -> s4 { value.to_bits() as s4 } -pub fn intBitsToFloat(_env: NonNull, bits: s4) -> f32 { +pub fn intBitsToFloat(_env: NonNull, _class: &'static Class, bits: s4) -> f32 { f32::from_bits(bits as u4) } diff --git a/runtime/src/native/java/lang/StringUTF16.rs b/runtime/src/native/java/lang/StringUTF16.rs index c545f5b..b7f6386 100644 --- a/runtime/src/native/java/lang/StringUTF16.rs +++ b/runtime/src/native/java/lang/StringUTF16.rs @@ -1,4 +1,5 @@ use crate::include_generated; +use crate::objects::class::Class; use std::ptr::NonNull; @@ -7,6 +8,6 @@ use common::int_types::s4; include_generated!("native/java/lang/def/StringUTF16.definitions.rs"); -pub fn isBigEndian(_env: NonNull) -> s4 { +pub fn isBigEndian(_env: NonNull, _class: &'static Class) -> s4 { s4::from(cfg!(target_endian = "big")) } diff --git a/runtime/src/native/java/lang/System.rs b/runtime/src/native/java/lang/System.rs index fe4e5d7..0df62e5 100644 --- a/runtime/src/native/java/lang/System.rs +++ b/runtime/src/native/java/lang/System.rs @@ -1,4 +1,4 @@ -use crate::classpath::classloader::ClassLoader; +use crate::objects::class::Class; use crate::objects::reference::Reference; use crate::thread::JavaThread; @@ -14,24 +14,33 @@ use symbols::sym; include_generated!("native/java/lang/def/System.registerNatives.rs"); include_generated!("native/java/lang/def/System.definitions.rs"); -pub fn setIn0(_: NonNull, in_: Reference /* java.io.InputStream */) { - let class = ClassLoader::lookup_class(sym!(java_lang_System)).unwrap(); +pub fn setIn0( + _: NonNull, + class: &'static Class, + in_: Reference, // java.io.InputStream +) { let field = class .fields() .find(|field| field.name == sym!(r#in) && field.descriptor.is_class(b"java/io/InputStream")) .expect("java/lang/System#in field should exist"); field.set_static_value(Operand::Reference(in_)); } -pub fn setOut0(_env: NonNull, out: Reference /* java.io.PrintStream */) { - let class = ClassLoader::lookup_class(sym!(java_lang_System)).unwrap(); +pub fn setOut0( + _env: NonNull, + class: &'static Class, + out: Reference, // java.io.PrintStream +) { let field = class .fields() .find(|field| field.name == sym!(out) && field.descriptor.is_class(b"java/io/PrintStream")) .expect("java/lang/System#out field should exist"); field.set_static_value(Operand::Reference(out)); } -pub fn setErr0(_env: NonNull, err: Reference /* java.io.PrintStream */) { - let class = ClassLoader::lookup_class(sym!(java_lang_System)).unwrap(); +pub fn setErr0( + _env: NonNull, + class: &'static Class, + err: Reference, // java.io.PrintStream +) { let field = class .fields() .find(|field| field.name == sym!(err) && field.descriptor.is_class(b"java/io/PrintStream")) @@ -39,11 +48,11 @@ pub fn setErr0(_env: NonNull, err: Reference /* java.io.PrintStream */) field.set_static_value(Operand::Reference(err)); } -pub fn currentTimeMillis(_env: NonNull) -> jlong { +pub fn currentTimeMillis(_env: NonNull, _class: &'static Class) -> jlong { unimplemented!("System#currentTimeMillis") } -pub fn nanoTime(_env: NonNull) -> jlong { +pub fn nanoTime(_env: NonNull, _class: &'static Class) -> jlong { let time_nanos = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("current system time should not be before the UNIX epoch") @@ -54,6 +63,7 @@ pub fn nanoTime(_env: NonNull) -> jlong { pub fn arraycopy( env: NonNull, + _class: &'static Class, src: Reference, // java.lang.Object src_pos: jint, dest: Reference, // java.lang.Object @@ -90,10 +100,18 @@ pub fn arraycopy( ); } -pub fn identityHashCode(env: NonNull, x: Reference /* java.lang.Object */) -> jint { +pub fn identityHashCode( + env: NonNull, + _class: &'static Class, + x: Reference, // java.lang.Object +) -> jint { crate::native::java::lang::Object::hashCode(env, x) } -pub fn mapLibraryName(_env: NonNull, _libname: Reference) -> Reference { +pub fn mapLibraryName( + _env: NonNull, + _class: &'static Class, + _libname: Reference, +) -> Reference { unimplemented!("System#mapLibraryName") } diff --git a/runtime/src/native/java/lang/Thread.rs b/runtime/src/native/java/lang/Thread.rs index 6f87541..77c54d1 100644 --- a/runtime/src/native/java/lang/Thread.rs +++ b/runtime/src/native/java/lang/Thread.rs @@ -1,4 +1,6 @@ +use crate::objects::class::Class; use crate::objects::reference::Reference; +use crate::thread::java_lang_Thread::ThreadStatus; use crate::thread::pool::ThreadPool; use crate::thread::{java_lang_Thread, JavaThread, JavaThreadBuilder}; @@ -6,22 +8,24 @@ use std::cmp; use std::ptr::NonNull; use std::sync::atomic::AtomicUsize; -use crate::thread::java_lang_Thread::ThreadStatus; use ::jni::env::JniEnv; use ::jni::sys::{jboolean, jint, jlong}; include_generated!("native/java/lang/def/Thread.registerNatives.rs"); include_generated!("native/java/lang/def/Thread.definitions.rs"); -pub fn findScopedValueBindings(_env: NonNull) -> Reference /* java.lang.Object */ { +pub fn findScopedValueBindings(_env: NonNull, _class: &'static Class) -> Reference /* java.lang.Object */ +{ unimplemented!("java.lang.Thread#findScopedValueBindings"); } -pub fn currentCarrierThread(_env: NonNull) -> Reference /* java.lang.Thread */ { +pub fn currentCarrierThread(_env: NonNull, _class: &'static Class) -> Reference /* java.lang.Thread */ +{ unimplemented!("java.lang.Thread#currentCarrierThread"); } -pub fn currentThread(env: NonNull) -> Reference /* java.lang.Thread */ { +pub fn currentThread(env: NonNull, _class: &'static Class) -> Reference /* java.lang.Thread */ +{ unsafe { let thread = JavaThread::for_env(env.as_ptr() as _); (*thread).obj().expect("current thread should exist") @@ -36,27 +40,33 @@ pub fn setCurrentThread( unimplemented!("java.lang.Thread#setCurrentThread"); } -pub fn scopedValueCache(_env: NonNull) -> Reference /* []java.lang.Object */ { +pub fn scopedValueCache(_env: NonNull, _class: &'static Class) -> Reference /* []java.lang.Object */ +{ unimplemented!("java.lang.Thread#scopedValueCache"); } -pub fn setScopedValueCache(_env: NonNull, _cache: Reference /* []java.lang.Object */) { +pub fn setScopedValueCache( + _env: NonNull, + _class: &'static Class, + _cache: Reference, // []java.lang.Object +) { unimplemented!("java.lang.Thread#setScopedValueCache"); } pub fn ensureMaterializedForStackWalk( _env: NonNull, + _class: &'static Class, _o: Reference, // java.lang.Object ) { unimplemented!("java.lang.Thread#ensureMaterializedForStackWalk"); } -pub fn yield0(_env: NonNull) { +pub fn yield0(_env: NonNull, _class: &'static Class) { std::thread::yield_now(); } // throws InterruptedException -pub fn sleepNanos0(_env: NonNull, _nanos: jlong) { +pub fn sleepNanos0(_env: NonNull, _class: &'static Class, _nanos: jlong) { unimplemented!("java.lang.Thread#sleepNanos0"); } @@ -86,7 +96,11 @@ pub fn start0(_env: NonNull, this: Reference /* java.lang.Thread */) { java_lang_Thread::holder::set_thread_status(obj, ThreadStatus::Runnable); } -pub fn holdsLock(_env: NonNull, _obj: Reference /* java.lang.Object */) -> jboolean { +pub fn holdsLock( + _env: NonNull, + _class: &'static Class, + _obj: Reference, // java.lang.Object +) -> jboolean { unimplemented!("java.lang.Thread#HoldsLock"); } @@ -100,13 +114,15 @@ pub fn getStackTrace0( pub fn dumpThreads( _env: NonNull, + _class: &'static Class, _threads: Reference, // []java.lang.Thread ) -> Reference /* [][]java.lang.StackTraceElement */ { unimplemented!("java.lang.Thread#dumpThreads"); } -pub fn getThreads(_env: NonNull) -> Reference /* []java.lang.Thread */ { +pub fn getThreads(_env: NonNull, _class: &'static Class) -> Reference /* []java.lang.Thread */ +{ unimplemented!("java.lang.Thread#getThreads"); } @@ -130,7 +146,7 @@ pub fn interrupt0(_env: NonNull, _this: Reference /* java.lang.Thread */ unimplemented!("java.lang.Thread#interrupt"); } -pub fn clearInterruptEvent(_env: NonNull) { +pub fn clearInterruptEvent(_env: NonNull, _class: &'static Class) { unimplemented!("java.lang.Thread#clearInterruptEvent"); } @@ -142,7 +158,7 @@ pub fn setNativeName( unimplemented!("java.lang.Thread#setNativeName"); } -pub fn getNextThreadIdOffset(_env: NonNull) -> jlong { +pub fn getNextThreadIdOffset(_env: NonNull, _class: &'static Class) -> jlong { // https://github.com/openjdk/jdk/blob/a3b58ee5cd1ec0ea78649d4128d272458b05eb13/src/java.base/share/classes/java/lang/Thread.java#L624-L627 const INITIAL_THREAD_ID: usize = 3; static NEXT_THREAD_ID: AtomicUsize = AtomicUsize::new(INITIAL_THREAD_ID); diff --git a/runtime/src/native/java/lang/Throwable.rs b/runtime/src/native/java/lang/Throwable.rs index 22174d7..baaf085 100644 --- a/runtime/src/native/java/lang/Throwable.rs +++ b/runtime/src/native/java/lang/Throwable.rs @@ -1,6 +1,4 @@ -use crate::classpath::classloader::ClassLoader; use crate::objects::array::{ArrayContent, ArrayInstance}; -use crate::objects::class::Class; use crate::objects::instance::Instance; use crate::objects::method::Method; use crate::objects::reference::Reference; @@ -8,13 +6,11 @@ use crate::thread::frame::stack::VisibleStackFrame; use crate::thread::JavaThread; use std::ptr::NonNull; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::Ordering; use std::sync::Once; use ::jni::env::JniEnv; use ::jni::sys::{jint, jlong}; -use classfile::FieldType; -use common::box_slice; use common::int_types::s4; use common::traits::PtrType; use instructions::Operand; @@ -22,7 +18,6 @@ use symbols::sym; #[allow(non_upper_case_globals)] mod stacktrace_element { - use crate::classpath::classloader::ClassLoader; use crate::objects::class::Class; use crate::objects::class_instance::ClassInstance; use crate::objects::constant_pool::cp_types; @@ -35,7 +30,6 @@ mod stacktrace_element { use common::traits::PtrType; use instructions::Operand; - use symbols::sym; unsafe fn initialize(class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); @@ -47,12 +41,8 @@ mod stacktrace_element { return; } - let stack_trace_element_class = ClassLoader::Bootstrap - .load(sym!(java_lang_StackTraceElement)) - .unwrap(); - unsafe { - crate::globals::classes::set_java_lang_StackTraceElement(stack_trace_element_class); + crate::globals::classes::set_java_lang_StackTraceElement(class); crate::globals::fields::java_lang_StackTraceElement::init_offsets(); } } @@ -252,12 +242,11 @@ pub fn fillInStackTrace( // Create the backtrace let backtrace_depth = stack_depth - frames_to_skip; let mut backtrace = BackTrace::new(backtrace_depth); - for (idx, frame) in current_thread + for frame in current_thread .frame_stack() .iter() .skip(frames_to_skip) .take(backtrace_depth) - .enumerate() { backtrace.push(frame); } diff --git a/runtime/src/native/java/lang/ref/Finalizer.rs b/runtime/src/native/java/lang/ref/Finalizer.rs index a126871..eb472f4 100644 --- a/runtime/src/native/java/lang/ref/Finalizer.rs +++ b/runtime/src/native/java/lang/ref/Finalizer.rs @@ -1,3 +1,4 @@ +use crate::objects::class::Class; use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -7,12 +8,13 @@ use jni::sys::jboolean; include_generated!("native/java/lang/ref/def/Finalizer.definitions.rs"); -pub fn isFinalizationEnabled(_: NonNull) -> jboolean { +pub fn isFinalizationEnabled(_: NonNull, _class: &'static Class) -> jboolean { false // finalization is deprecated anyway } pub fn reportComplete( _: NonNull, + _class: &'static Class, _finalizee: Reference, // java.lang.Object ) { unimplemented!("java.lang.ref.Finalizer#reportComplete") diff --git a/runtime/src/native/java/lang/ref/Reference.rs b/runtime/src/native/java/lang/ref/Reference.rs index fd724d6..89cdde4 100644 --- a/runtime/src/native/java/lang/ref/Reference.rs +++ b/runtime/src/native/java/lang/ref/Reference.rs @@ -1,3 +1,4 @@ +use crate::objects::class::Class; use crate::objects::instance::Instance; use crate::objects::reference::Reference; @@ -8,16 +9,16 @@ use jni::sys::jboolean; include_generated!("native/java/lang/ref/def/Reference.definitions.rs"); -pub fn getAndClearReferencePendingList(_: NonNull) -> Reference /* java.lang.ref.Reference */ +pub fn getAndClearReferencePendingList(_: NonNull, _class: &'static Class) -> Reference /* java.lang.ref.Reference */ { unimplemented!("java.lang.ref.Reference#getAndClearReferencePendingList") } -pub fn hasReferencePendingList(_: NonNull) -> jboolean { +pub fn hasReferencePendingList(_: NonNull, _class: &'static Class) -> jboolean { unimplemented!("java.lang.ref.Reference#hasReferencePendingList") } -pub fn waitForReferencePendingList(_: NonNull) { +pub fn waitForReferencePendingList(_: NonNull, _class: &'static Class) { unimplemented!("java.lang.ref.Reference#waitForReferencePendingList") } diff --git a/runtime/src/native/java/security/AccessController.rs b/runtime/src/native/java/security/AccessController.rs index 76b2c81..3a27ef8 100644 --- a/runtime/src/native/java/security/AccessController.rs +++ b/runtime/src/native/java/security/AccessController.rs @@ -1,3 +1,4 @@ +use crate::objects::class::Class; use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -8,6 +9,7 @@ include_generated!("native/java/security/def/AccessController.definitions.rs"); pub fn getProtectionDomain( _env: NonNull, + _this_class: &'static Class, _class: Reference, // java.lang.Class ) -> Reference /* java.security.ProtectionDomain */ { unimplemented!("java.security.AccessController#getProtectionDomain"); @@ -15,19 +17,23 @@ pub fn getProtectionDomain( pub fn ensureMaterializedForStackWalk( _env: NonNull, + _this_class: &'static Class, _class: Reference, // java.lang.Object ) { unimplemented!("java.security.AccessController#ensureMaterializedForStackWalk") } -pub fn getStackAccessControlContext(_env: NonNull) -> Reference /* java.security.AccessControlContext */ +pub fn getStackAccessControlContext(_env: NonNull, _class: &'static Class) -> Reference /* java.security.AccessControlContext */ { // TODO: Actually implement this tracing::warn!(target: "java.security.AccessController#getStackAccessContext", "Assuming no privileged stack"); Reference::null() } -pub fn getInheritedAccessControlContext(_env: NonNull) -> Reference /* java.security.AccessControlContext */ +pub fn getInheritedAccessControlContext( + _env: NonNull, + _class: &'static Class, +) -> Reference /* java.security.AccessControlContext */ { unimplemented!("java.security.AccessController#getInheritedAccessControlContext"); } diff --git a/runtime/src/native/jdk/internal/loader/BootLoader.rs b/runtime/src/native/jdk/internal/loader/BootLoader.rs index 3582630..af564a8 100644 --- a/runtime/src/native/jdk/internal/loader/BootLoader.rs +++ b/runtime/src/native/jdk/internal/loader/BootLoader.rs @@ -1,3 +1,6 @@ +use crate::classpath::classloader::ClassLoader; +use crate::modules::Module; +use crate::objects::class::Class; use crate::objects::instance::Instance; use crate::objects::reference::Reference; use crate::thread::JavaThread; @@ -10,7 +13,8 @@ include_generated!("native/jdk/internal/loader/def/BootLoader.definitions.rs"); /// Returns an array of the binary name of the packages defined by the boot loader, in VM /// internal form (forward slashes instead of dot). -pub fn getSystemPackageNames(_env: NonNull) -> Reference /* String[] */ { +pub fn getSystemPackageNames(_env: NonNull, _class: &'static Class) -> Reference /* String[] */ +{ unimplemented!("jdk.internal.loader.BootLoader#getSystemPackageNames") } @@ -21,13 +25,21 @@ pub fn getSystemPackageNames(_env: NonNull) -> Reference /* String[] */ /// append path (i.e. -Xbootclasspath/a or BOOT-CLASS-PATH attribute specified in java agent). pub fn getSystemPackageLocation( _env: NonNull, + _class: &'static Class, _name: Reference, // java.lang.String ) -> Reference /* java.lang.String */ { unimplemented!("jdk.internal.loader.BootLoader#getSystemPackageLocation") } +/// # Throws +/// +/// `IllegalArgumentException` is thrown if: +/// * Module is named +/// * Module is not an instance or subclass of j.l.r.Module +/// * Module is not loaded by the bootLoader pub fn setBootLoaderUnnamedModule0( env: NonNull, + _class: &'static Class, module: Reference, // java.lang.Module ) { if module.is_null() { @@ -56,5 +68,6 @@ pub fn setBootLoaderUnnamedModule0( panic!("IllegalArgumentException"); // TODO } - tracing::warn!("(!!!) UNIMPLEMENTED jdk.internal.loader.BootLoader#setBootLoaderUnnamedModule0") + let module = Module::unnamed(module); + ClassLoader::set_bootloader_unnamed_module(module); } diff --git a/runtime/src/native/jdk/internal/loader/NativeLibraries.rs b/runtime/src/native/jdk/internal/loader/NativeLibraries.rs index f20c7f8..06c4c3e 100644 --- a/runtime/src/native/jdk/internal/loader/NativeLibraries.rs +++ b/runtime/src/native/jdk/internal/loader/NativeLibraries.rs @@ -1,3 +1,4 @@ +use crate::objects::class::Class; use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -9,6 +10,7 @@ include_generated!("native/jdk/internal/loader/def/NativeLibraries.definitions.r pub fn load( _env: NonNull, + _class: &'static Class, _impl_: Reference, // jdk.internal.loader.NativeLibraries$NativeLibraryImpl _name: Reference, // java.lang.String _is_builtin: jboolean, @@ -19,6 +21,7 @@ pub fn load( pub fn unload( _env: NonNull, + _class: &'static Class, _name: Reference, // java.lang.String _is_builtin: jboolean, _handle: jlong, @@ -28,6 +31,7 @@ pub fn unload( pub fn findBuiltinLib( _env: NonNull, + _class: &'static Class, _name: Reference, // java.lang.String ) -> Reference /* java.lang.String */ { diff --git a/runtime/src/native/jdk/internal/misc/CDS.rs b/runtime/src/native/jdk/internal/misc/CDS.rs index 9a56470..8a9139b 100644 --- a/runtime/src/native/jdk/internal/misc/CDS.rs +++ b/runtime/src/native/jdk/internal/misc/CDS.rs @@ -1,3 +1,4 @@ +use crate::objects::class::Class; use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -7,7 +8,7 @@ use ::jni::sys::{jint, jlong}; include_generated!("native/jdk/internal/misc/def/CDS.definitions.rs"); -pub fn getCDSConfigStatus(_: NonNull) -> jint { +pub fn getCDSConfigStatus(_: NonNull, _class: &'static Class) -> jint { // TODO: Bitfield of: // private static final int IS_DUMPING_ARCHIVE = 1 << 0; // private static final int IS_DUMPING_STATIC_ARCHIVE = 1 << 1; @@ -16,30 +17,35 @@ pub fn getCDSConfigStatus(_: NonNull) -> jint { 0 } -pub fn logLambdaFormInvoker(_: NonNull, _line: Reference) { +pub fn logLambdaFormInvoker(_: NonNull, _class: &'static Class, _line: Reference) { unimplemented!("jdk.internal.misc.CDS#logLambdaFormInvoker") } -pub fn initializeFromArchive(_: NonNull, _class: Reference) { +pub fn initializeFromArchive(_: NonNull, _this_class: &'static Class, _class: Reference) { // TODO } pub fn defineArchivedModules( _: NonNull, + _class: &'static Class, _platform_loader: Reference, _system_loader: Reference, ) { unimplemented!("jdk.internal.misc.CDS#defineArchivedModules") } -pub fn getRandomSeedForDumping(_: NonNull) -> jlong { +pub fn getRandomSeedForDumping(_: NonNull, _class: &'static Class) -> jlong { // TODO: https://github.com/openjdk/jdk/blob/af564e46b006fcd57ec7391cd1438b3b9311b1d6/src/hotspot/share/prims/jvm.cpp#L3696 0 } -pub fn dumpClassList(_: NonNull, _list_file_name: Reference) { +pub fn dumpClassList(_: NonNull, _class: &'static Class, _list_file_name: Reference) { unimplemented!("jdk.internal.misc.CDS#dumpClassList") } -pub fn dumpDynamicArchive(_: NonNull, _archive_file_name: Reference) { +pub fn dumpDynamicArchive( + _: NonNull, + _class: &'static Class, + _archive_file_name: Reference, +) { unimplemented!("jdk.internal.misc.CDS#dumpDynamicArchive") } diff --git a/runtime/src/native/jdk/internal/misc/Signal.rs b/runtime/src/native/jdk/internal/misc/Signal.rs index 4aa5a5d..82f34d7 100644 --- a/runtime/src/native/jdk/internal/misc/Signal.rs +++ b/runtime/src/native/jdk/internal/misc/Signal.rs @@ -1,4 +1,5 @@ use crate::native::Reference; +use crate::objects::class::Class; use crate::string_interner::StringInterner; use std::ptr::NonNull; @@ -8,7 +9,11 @@ use ::jni::sys::{jint, jlong}; include_generated!("native/jdk/internal/misc/def/Signal.definitions.rs"); -pub fn findSignal0(_: NonNull, sig_name: Reference /* java.lang.String */) -> jint { +pub fn findSignal0( + _: NonNull, + _class: &'static Class, + sig_name: Reference, // java.lang.String +) -> jint { let sig_name_string = sig_name.extract_class(); let sig_name = StringInterner::rust_string_from_java_string(sig_name_string); @@ -18,7 +23,7 @@ pub fn findSignal0(_: NonNull, sig_name: Reference /* java.lang.String * } } -pub fn handle0(_: NonNull, sig: jint, native_h: jlong) -> jlong { +pub fn handle0(_: NonNull, _class: &'static Class, sig: jint, native_h: jlong) -> jlong { let signal = platform::Signal::from(sig); if !signal.registration_allowed() { @@ -43,6 +48,6 @@ pub fn handle0(_: NonNull, sig: jint, native_h: jlong) -> jlong { old.as_usize() as jlong } -pub fn raise0(_: NonNull, _sig: jint) { +pub fn raise0(_: NonNull, _class: &'static Class, _sig: jint) { unimplemented!("jdk.internal.misc.Signal#raise0"); } diff --git a/runtime/src/native/jdk/internal/misc/VM.rs b/runtime/src/native/jdk/internal/misc/VM.rs index 9b5bdb4..73b5981 100644 --- a/runtime/src/native/jdk/internal/misc/VM.rs +++ b/runtime/src/native/jdk/internal/misc/VM.rs @@ -1,4 +1,5 @@ use crate::include_generated; +use crate::objects::class::Class; use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -8,27 +9,29 @@ use common::int_types::s8; include_generated!("native/jdk/internal/misc/def/VM.definitions.rs"); -pub fn latestUserDefinedLoader0(_env: NonNull) -> Reference /* java.lang.ClassLoader */ { +pub fn latestUserDefinedLoader0(_env: NonNull, _class: &'static Class) -> Reference /* java.lang.ClassLoader */ +{ unimplemented!("jdk.internal.misc.VM#latestUserDefinedLoader0") } -pub fn getuid(_env: NonNull) -> s8 { +pub fn getuid(_env: NonNull, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#getuid") } -pub fn geteuid(_env: NonNull) -> s8 { +pub fn geteuid(_env: NonNull, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#geteuid") } -pub fn getgid(_env: NonNull) -> s8 { +pub fn getgid(_env: NonNull, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#getgid") } -pub fn getegid(_env: NonNull) -> s8 { +pub fn getegid(_env: NonNull, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#getegid") } -pub fn getNanoTimeAdjustment(_env: NonNull, _offset: s8) -> s8 { +pub fn getNanoTimeAdjustment(_env: NonNull, _class: &'static Class, _offset: s8) -> s8 { unimplemented!("jdk.internal.misc.VM#getNanoTimeAdjustment") } -pub fn getRuntimeArguments(_env: NonNull) -> Reference /* String[] */ { +pub fn getRuntimeArguments(_env: NonNull, _class: &'static Class) -> Reference /* String[] */ +{ unimplemented!("jdk.internal.misc.VM#getRuntimeArguments") } -pub fn initialize(_env: NonNull) { +pub fn initialize(_env: NonNull, _class: &'static Class) { // https://github.com/openjdk/jdk/blob/7abe26935ab4356de54acee93390a0d8be1ea289/src/java.base/share/native/libjava/VM.c#L44 } diff --git a/runtime/src/native/jdk/internal/reflect/Reflection.rs b/runtime/src/native/jdk/internal/reflect/Reflection.rs index 1f60a98..c46418a 100644 --- a/runtime/src/native/jdk/internal/reflect/Reflection.rs +++ b/runtime/src/native/jdk/internal/reflect/Reflection.rs @@ -1,3 +1,4 @@ +use crate::objects::class::Class; use crate::objects::reference::Reference; use crate::thread::JavaThread; @@ -9,7 +10,7 @@ use ::jni::sys::{jboolean, jint}; include_generated!("native/jdk/internal/reflect/def/Reflection.definitions.rs"); #[expect(clippy::match_same_arms)] -pub fn getCallerClass(env: NonNull) -> Reference { +pub fn getCallerClass(env: NonNull, _class: &'static Class) -> Reference { let current_thread = unsafe { &*JavaThread::for_env(env.as_ptr() as _) }; // The call stack at this point looks something like this: @@ -50,12 +51,17 @@ pub fn getCallerClass(env: NonNull) -> Reference { Reference::null() } -pub fn getClassAccessFlags(_env: NonNull, _class: Reference) -> jint { +pub fn getClassAccessFlags( + _env: NonNull, + _this_class: &'static Class, + _class: Reference, +) -> jint { unimplemented!("jdk.internal.reflect.Reflection#getClassAccessFlags") } pub fn areNestMates( _env: NonNull, + _class: &'static Class, _current_class: Reference, _member_class: Reference, ) -> jboolean { diff --git a/runtime/src/native/jdk/internal/util/SystemProps.rs b/runtime/src/native/jdk/internal/util/SystemProps.rs index 673e7ad..6c2d2fc 100644 --- a/runtime/src/native/jdk/internal/util/SystemProps.rs +++ b/runtime/src/native/jdk/internal/util/SystemProps.rs @@ -1,7 +1,7 @@ pub mod Raw { - use crate::classpath::classloader::ClassLoader; use crate::include_generated; use crate::objects::array::ArrayInstance; + use crate::objects::class::Class; use crate::objects::reference::Reference; use crate::string_interner::StringInterner; @@ -10,7 +10,6 @@ pub mod Raw { use ::jni::env::JniEnv; use common::traits::PtrType; use instructions::Operand; - use symbols::sym; include_generated!("native/jdk/internal/util/def/SystemProps$Raw.constants.rs"); include_generated!("native/jdk/internal/util/def/SystemProps.definitions.rs"); @@ -21,7 +20,8 @@ pub mod Raw { const VM_VERSION: &str = env!("CARGO_PKG_VERSION"); const VM_VENDOR: &str = env!("SYSTEM_PROPS_VM_VENDOR"); - pub fn vmProperties(_env: NonNull) -> Reference /* [Ljava/lang/String; */ { + pub fn vmProperties(_env: NonNull, _class: &'static Class) -> Reference /* [Ljava/lang/String; */ + { macro_rules! store_properties { ($prop_array:ident; $($key:literal => $value:expr),+ $(,)?) => { let mut index = 0; @@ -58,7 +58,8 @@ pub mod Raw { Reference::array(prop_array) } - pub fn platformProperties(env: NonNull) -> Reference /* [Ljava/lang/String; */ { + pub fn platformProperties(_env: NonNull, _class: &'static Class) -> Reference /* [Ljava/lang/String; */ + { macro_rules! store_properties { ($prop_array:ident; $($index:expr => $value:expr),+ $(,)?) => { $( diff --git a/runtime/src/native/jni/class.rs b/runtime/src/native/jni/class.rs index 01ed4d2..bd0555f 100644 --- a/runtime/src/native/jni/class.rs +++ b/runtime/src/native/jni/class.rs @@ -1,6 +1,7 @@ use super::{classref_from_jclass, IntoJni}; use crate::classpath::classloader::ClassLoader; use crate::objects::class::Class; +use crate::thread::JavaThread; use core::ffi::{c_char, CStr}; @@ -23,7 +24,17 @@ pub unsafe extern "system" fn DefineClass( pub unsafe extern "system" fn FindClass(env: *mut JNIEnv, name: *const c_char) -> jclass { let name = unsafe { CStr::from_ptr(name) }; - if let Some(class) = ClassLoader::lookup_class(Symbol::intern_bytes(name.to_bytes())) { + // Initially assume the system loader + let mut loader = ClassLoader::bootstrap(); + + let thread = JavaThread::current(); + let frame_stack = thread.frame_stack(); + if let Some(current_frame) = frame_stack.current() { + // If we can find a caller frame, use its loader instead + loader = current_frame.method().class().loader(); + } + + if let Some(class) = loader.lookup_class(Symbol::intern_bytes(name.to_bytes())) { return class.into_jni(); } diff --git a/runtime/src/native/lookup.rs b/runtime/src/native/lookup.rs index b0d264a..d5138ac 100644 --- a/runtime/src/native/lookup.rs +++ b/runtime/src/native/lookup.rs @@ -1,5 +1,5 @@ -use crate::classpath::classloader::ClassLoader; use crate::java_call; +use crate::native::NativeMethodPtr; use crate::objects::method::Method; use crate::objects::reference::Reference; use crate::string_interner::StringInterner; @@ -201,17 +201,16 @@ fn lookup_style( num_args: usize, include_long: bool, os_style: bool, -) -> Option<*const c_void> { - let class_loader = method.class().loader; - if class_loader == ClassLoader::Bootstrap { +) -> Option { + let class_loader = method.class().loader(); + if class_loader.is_bootstrap() { if let Some(entry) = crate::native::lookup_method_opt(method) { - return Some(entry as _); + return Some(entry); } } - assert_eq!( - class_loader, - ClassLoader::Bootstrap, + assert!( + class_loader.is_bootstrap(), "Custom classloaders are not implemented yet" ); @@ -242,10 +241,10 @@ fn lookup_style( } let entry = address as usize as *const c_void; - Some(entry) + Some(unsafe { NativeMethodPtr::from_raw(entry) }) } -fn lookup_entry(method: &Method, thread: &JavaThread) -> Option<*const c_void> { +fn lookup_entry(method: &Method, thread: &JavaThread) -> Option { let mut name_converter = NativeNameConverter::new(method); // Compute pure name @@ -315,11 +314,11 @@ fn lookup_entry(method: &Method, thread: &JavaThread) -> Option<*const c_void> { } /// Check if there are any JVM TI prefixes which have been applied to the native method name. -fn lookup_entry_prefixed(_method: &Method, _thread: &JavaThread) -> Option<*const c_void> { +fn lookup_entry_prefixed(_method: &Method, _thread: &JavaThread) -> Option { todo!() } -fn lookup_base(method: &Method, thread: &JavaThread) -> *const c_void { +fn lookup_base(method: &Method, thread: &JavaThread) -> NativeMethodPtr { if let Some(entry) = lookup_entry(method, thread) { return entry; } diff --git a/runtime/src/native/mod.rs b/runtime/src/native/mod.rs index 17d74bf..fbcae06 100644 --- a/runtime/src/native/mod.rs +++ b/runtime/src/native/mod.rs @@ -4,11 +4,13 @@ pub mod intrinsics; pub mod jni; pub mod lookup; +use crate::objects::class::Class; use crate::objects::method::Method; use crate::objects::reference::Reference; use crate::stack::local_stack::LocalStack; use std::collections::HashMap; +use std::ffi::c_void; use std::fmt::Debug; use std::ptr::NonNull; use std::sync::{LazyLock, RwLock}; @@ -22,6 +24,7 @@ pub struct NativeMethodDef { pub class: Symbol, pub name: Symbol, pub descriptor: Symbol, + pub is_static: bool, } impl Debug for NativeMethodDef { @@ -47,7 +50,50 @@ macro_rules! include_generated { } pub type NativeReturn = Option>; -pub type NativeMethodPtr = fn(NonNull, LocalStack) -> NativeReturn; +pub type NativeStaticMethodPtr = fn(NonNull, &'static Class, LocalStack) -> NativeReturn; +pub type NativeNonStaticMethodPtr = fn(NonNull, LocalStack) -> NativeReturn; + +#[derive(Copy, Clone)] +union NativeMethodPtrInner { + _static: NativeStaticMethodPtr, + non_static: NativeNonStaticMethodPtr, +} + +#[derive(Copy, Clone)] +pub struct NativeMethodPtr { + inner: NativeMethodPtrInner, +} + +impl NativeMethodPtr { + /// Used by the native method generator, should never be used directly. + #[doc(hidden)] + pub fn new_static(f: NativeStaticMethodPtr) -> NativeMethodPtr { + Self { + inner: NativeMethodPtrInner { _static: f }, + } + } + /// Used by the native method generator, should never be used directly. + #[doc(hidden)] + pub fn new_non_static(f: NativeNonStaticMethodPtr) -> NativeMethodPtr { + Self { + inner: NativeMethodPtrInner { non_static: f }, + } + } + + pub unsafe fn from_raw(ptr: *const c_void) -> Self { + Self { + inner: unsafe { core::mem::transmute::<*const c_void, NativeMethodPtrInner>(ptr) }, + } + } + + pub unsafe fn as_static(self) -> NativeStaticMethodPtr { + unsafe { self.inner._static } + } + + pub unsafe fn as_non_static(self) -> NativeNonStaticMethodPtr { + unsafe { self.inner.non_static } + } +} include!("../../../generated/native/native_init.rs"); // Provides `init_native_method_table()`, generated by `runtime/build.rs` pub(self) static NATIVE_METHOD_TABLE: LazyLock>> = @@ -68,6 +114,7 @@ pub fn lookup_method_opt(method: &Method) -> Option { class: method.class().name, name: method.name, descriptor: method.descriptor, + is_static: method.is_static(), }; NATIVE_METHOD_TABLE @@ -84,28 +131,6 @@ pub(self) fn insert_method((def, ptr): (NativeMethodDef, NativeMethodPtr)) { // Module marker, do not remove -pub(crate) mod jdk { - pub(crate) mod internal { - pub(crate) mod misc { - pub(crate) mod ScopedMemoryAccess; - pub(crate) mod CDS; - pub(crate) mod VM; - pub(crate) mod Unsafe; - pub(crate) mod Signal; - } - pub(crate) mod util { - pub(crate) mod SystemProps; - } - pub(crate) mod loader { - pub(crate) mod NativeLibraries; - pub(crate) mod BootLoader; - } - pub(crate) mod reflect { - pub(crate) mod Reflection; - } - } -} - pub(crate) mod java { pub(crate) mod io { pub(crate) mod FileInputStream; @@ -136,3 +161,25 @@ pub(crate) mod java { } } +pub(crate) mod jdk { + pub(crate) mod internal { + pub(crate) mod misc { + pub(crate) mod ScopedMemoryAccess; + pub(crate) mod CDS; + pub(crate) mod VM; + pub(crate) mod Unsafe; + pub(crate) mod Signal; + } + pub(crate) mod util { + pub(crate) mod SystemProps; + } + pub(crate) mod loader { + pub(crate) mod NativeLibraries; + pub(crate) mod BootLoader; + } + pub(crate) mod reflect { + pub(crate) mod Reflection; + } + } +} + diff --git a/runtime/src/objects/array.rs b/runtime/src/objects/array.rs index 2ec5359..b02ce4c 100644 --- a/runtime/src/objects/array.rs +++ b/runtime/src/objects/array.rs @@ -57,7 +57,7 @@ impl ArrayInstance { _ => panic!("Invalid array type code: {}", type_code), }; - let array_class = ClassLoader::Bootstrap.load(array_signature).unwrap(); + let array_class = ClassLoader::bootstrap().load(array_signature).unwrap(); let elements = ArrayContent::default_initialize(type_code, count); ArrayInstancePtr::new(Self { @@ -74,7 +74,7 @@ impl ArrayInstance { } let array_class_name = component_class.array_class_name(); - let array_class = ClassLoader::Bootstrap.load(array_class_name).unwrap(); + let array_class = component_class.loader().load(array_class_name).unwrap(); let elements = box_slice![Reference::null(); count as usize]; ArrayInstancePtr::new(Self { diff --git a/runtime/src/objects/class/mod.rs b/runtime/src/objects/class/mod.rs index 0cbe84e..da8d2e1 100644 --- a/runtime/src/objects/class/mod.rs +++ b/runtime/src/objects/class/mod.rs @@ -113,7 +113,7 @@ impl FieldContainer { pub struct Class { pub name: Symbol, pub access_flags: ClassAccessFlags, - pub loader: ClassLoader, + loader: &'static ClassLoader, pub super_class: Option<&'static Class>, pub interfaces: Vec<&'static Class>, misc_cache: UnsafeCell, @@ -193,6 +193,11 @@ impl Class { unsafe { (&*self.class_ty.get()).assume_init_ref() } } + /// Get a reference to the `ClassLoader` that loaded this class + pub fn loader(&self) -> &ClassLoader { + self.loader + } + /// Get a reference to the constant pool for this class /// /// This returns an `Option`, as array classes do not have an associated constant pool. It is @@ -455,8 +460,8 @@ impl Class { // TC and SC are reference types, and type SC can be cast to TC by these run-time rules. // It's impossible to get a reference to an unloaded class - let S_class = ClassLoader::lookup_class(source_component).unwrap(); - let T_class = ClassLoader::lookup_class(dest_component).unwrap(); + let S_class = S_class.loader().lookup_class(source_component).unwrap(); + let T_class = T_class.loader().lookup_class(dest_component).unwrap(); return S_class.can_cast_to(T_class); } @@ -474,7 +479,7 @@ impl Class { pub unsafe fn new( parsed_file: ClassFile, super_class: Option<&'static Class>, - loader: ClassLoader, + loader: &'static ClassLoader, ) -> &'static Class { let access_flags = parsed_file.access_flags; let class_name_index = parsed_file.this_class; @@ -594,7 +599,7 @@ impl Class { pub unsafe fn new_array( name: Symbol, component: FieldType, - loader: ClassLoader, + loader: &'static ClassLoader, ) -> &'static Class { let dimensions = name .as_str() @@ -614,12 +619,8 @@ impl Class { super_class: Some(crate::globals::classes::java_lang_Object()), // https://docs.oracle.com/javase/specs/jls/se19/html/jls-4.html#jls-4.10.3 interfaces: vec![ - ClassLoader::Bootstrap - .load(sym!(java_lang_Cloneable)) - .unwrap(), - ClassLoader::Bootstrap - .load(sym!(java_io_Serializable)) - .unwrap(), + crate::globals::classes::java_lang_Cloneable(), + crate::globals::classes::java_io_Serializable(), ], misc_cache: UnsafeCell::new(MiscCache::default()), mirror: UnsafeCell::new(MaybeUninit::uninit()), // Set later diff --git a/runtime/src/objects/constant_pool/cp_types.rs b/runtime/src/objects/constant_pool/cp_types.rs index a4d75b2..6c7acb2 100644 --- a/runtime/src/objects/constant_pool/cp_types.rs +++ b/runtime/src/objects/constant_pool/cp_types.rs @@ -57,7 +57,7 @@ impl EntryType for Class { let entry = ClassName::resolve(class, cp, index); let name = ClassName::resolved_entry(entry); - let class = class.loader.load(name).unwrap(); + let class = class.loader().load(name).unwrap(); ResolvedEntry { class } } } diff --git a/runtime/src/objects/method/mod.rs b/runtime/src/objects/method/mod.rs index 5ad998a..0d56e44 100644 --- a/runtime/src/objects/method/mod.rs +++ b/runtime/src/objects/method/mod.rs @@ -47,7 +47,7 @@ pub struct Method { pub parameter_count: u1, pub line_number_table: Vec, pub code: Code, - native_method: RwLock<*const c_void>, + native_method: RwLock>, } impl PartialEq for Method { @@ -116,7 +116,7 @@ impl Method { parameter_count, line_number_table, code, - native_method: RwLock::new(std::ptr::null()), + native_method: RwLock::new(None), }; Box::leak(Box::new(method)) @@ -166,18 +166,14 @@ impl Method { } pub fn native_method(&self) -> Option { - let native_method = self.native_method.read().unwrap(); - if native_method.is_null() { - return None; - } - assert!(self.is_native()); - Some(unsafe { core::mem::transmute(*native_method) }) + let native_method = self.native_method.read().unwrap(); + *native_method } - pub fn set_native_method(&self, func: *const c_void) { + pub fn set_native_method(&self, func: NativeMethodPtr) { let mut lock = self.native_method.write().unwrap(); - *lock = func; + *lock = Some(func); } } diff --git a/runtime/src/objects/mirror.rs b/runtime/src/objects/mirror.rs index 7e71524..2c7dc87 100644 --- a/runtime/src/objects/mirror.rs +++ b/runtime/src/objects/mirror.rs @@ -1,8 +1,10 @@ use super::instance::{Header, Instance}; +use crate::modules::Module; use crate::objects::class::Class; use crate::objects::field::Field; use crate::objects::reference::{MirrorInstanceRef, Reference}; +use std::cell::UnsafeCell; use std::fmt::{Debug, Formatter}; use std::ptr::NonNull; @@ -28,11 +30,10 @@ enum MirrorTarget { /// ``` /// /// `c` is a mirror instance, with a target of `java.lang.String`. -#[derive(PartialEq)] pub struct MirrorInstance { header: Header, class: &'static Class, - pub fields: Box<[Operand]>, + fields: Box<[UnsafeCell>]>, target: MirrorTarget, } @@ -49,7 +50,7 @@ impl Debug for MirrorInstance { impl MirrorInstance { pub fn new(target: &'static Class) -> MirrorInstanceRef { let mirror_class = crate::globals::classes::java_lang_Class(); - let fields = Self::initialize_fields(mirror_class); + let fields = Self::initialize_fields(mirror_class, target); MirrorInstancePtr::new(Self { header: Header::new(), class: mirror_class, @@ -60,7 +61,7 @@ impl MirrorInstance { pub fn new_array(target: &'static Class) -> MirrorInstanceRef { let mirror_class = crate::globals::classes::java_lang_Class(); - let fields = Self::initialize_fields(mirror_class); + let fields = Self::initialize_fields(mirror_class, target); MirrorInstancePtr::new(Self { header: Header::new(), class: mirror_class, @@ -76,7 +77,9 @@ impl MirrorInstance { ); let mirror_class = crate::globals::classes::java_lang_Class(); - let fields = Self::initialize_fields(mirror_class); + let target_class = Self::target_for_primitive(&target); + + let fields = Self::initialize_fields(mirror_class, target_class); MirrorInstancePtr::new(Self { header: Header::new(), class: mirror_class, @@ -122,28 +125,55 @@ impl MirrorInstance { pub fn target_class(&self) -> &'static Class { match &self.target { MirrorTarget::Class(class) => *class, - MirrorTarget::Primitive(field_ty) => match field_ty { - FieldType::Byte => crate::globals::classes::java_lang_Byte(), - FieldType::Char => crate::globals::classes::java_lang_Character(), - FieldType::Double => crate::globals::classes::java_lang_Double(), - FieldType::Float => crate::globals::classes::java_lang_Float(), - FieldType::Int => crate::globals::classes::java_lang_Integer(), - FieldType::Long => crate::globals::classes::java_lang_Long(), - FieldType::Short => crate::globals::classes::java_lang_Short(), - FieldType::Boolean => crate::globals::classes::java_lang_Boolean(), - FieldType::Void => crate::globals::classes::java_lang_Void(), - _ => unreachable!("only primitive types should exist within primitive mirrors"), - }, + MirrorTarget::Primitive(field_ty) => Self::target_for_primitive(field_ty), + } + } + + pub fn set_module(&self, module: Reference) { + let module_offset = crate::globals::fields::java_lang_Class::module_field_offset(); + let ptr = self.fields[module_offset].get(); + unsafe { + assert!( + (&*ptr).expect_reference().is_null(), + "Attempt to set a module twice" + ); + *ptr = Operand::Reference(module); } } - fn initialize_fields(mirror_class: &'static Class) -> Box<[Operand]> { + fn target_for_primitive(primitive: &FieldType) -> &'static Class { + match primitive { + FieldType::Byte => crate::globals::classes::java_lang_Byte(), + FieldType::Char => crate::globals::classes::java_lang_Character(), + FieldType::Double => crate::globals::classes::java_lang_Double(), + FieldType::Float => crate::globals::classes::java_lang_Float(), + FieldType::Int => crate::globals::classes::java_lang_Integer(), + FieldType::Long => crate::globals::classes::java_lang_Long(), + FieldType::Short => crate::globals::classes::java_lang_Short(), + FieldType::Boolean => crate::globals::classes::java_lang_Boolean(), + FieldType::Void => crate::globals::classes::java_lang_Void(), + _ => unreachable!("only primitive types should exist within primitive mirrors"), + } + } + + fn initialize_fields( + mirror_class: &'static Class, + target_class: &'static Class, + ) -> Box<[UnsafeCell>]> { let instance_field_count = mirror_class.instance_field_count(); // Set the default values for our non-static fields let mut fields = Vec::with_capacity(instance_field_count); - for field in mirror_class.fields().filter(|field| !field.is_static()) { - fields.push(Field::default_value_for_ty(&field.descriptor)) + for field in mirror_class.instance_fields() { + fields.push(UnsafeCell::new(Field::default_value_for_ty( + &field.descriptor, + ))) + } + + let class_loader_offset = + crate::globals::fields::java_lang_Class::classLoader_field_offset(); + unsafe { + *fields[class_loader_offset].get() = Operand::Reference(target_class.loader().obj()); } fields.into_boxed_slice() @@ -167,7 +197,9 @@ impl Instance for MirrorInstance { ); } - self.fields[field_idx].clone() + let ptr = self.fields[field_idx].get(); + let value = unsafe { &*ptr }; + value.clone() } fn put_field_value(&mut self, field: &Field, value: Operand) { @@ -182,7 +214,8 @@ impl Instance for MirrorInstance { ); } - let current = &self.fields[field_idx]; + let ptr = self.fields[field_idx].get(); + let current = unsafe { &*ptr }; assert!( current.is_compatible_with(&value), "Expected type compatible with: {:?}, found: {:?}", @@ -190,7 +223,9 @@ impl Instance for MirrorInstance { value ); - self.fields[field_idx] = value; + unsafe { + *ptr = value; + } } unsafe fn get_field_value_raw(&self, field_idx: usize) -> NonNull> { diff --git a/runtime/src/objects/mod.rs b/runtime/src/objects/mod.rs index 9e8b470..66f6238 100644 --- a/runtime/src/objects/mod.rs +++ b/runtime/src/objects/mod.rs @@ -6,7 +6,6 @@ pub mod field; pub mod instance; pub mod method; pub mod mirror; -pub mod module; pub mod monitor; pub mod reference; pub mod vtable; diff --git a/runtime/src/objects/module.rs b/runtime/src/objects/module.rs deleted file mode 100644 index 0cdee3e..0000000 --- a/runtime/src/objects/module.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::sync::Arc; - -use common::int_types::u1; - -pub type ModuleRef = Arc; -pub type PackageRef = Arc; - -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] -pub struct Module { - name: Box<[u1]>, - - readable: Vec, - version: Box<[u1]>, - location: Box<[u1]>, - - open: bool, -} - -impl Module { - /// Whether the provided module is readable by this module - pub fn can_read(&self, other: ModuleRef) -> bool { - self.readable.contains(&other) - } - - /// Whether all packages in the module are unqualifiedly exported - /// - /// See [PackageExportType] - pub fn is_open(&self) -> bool { - self.open - } -} - -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] -pub enum PackageExportType { - /// Package is not exported - None, - /// Package is unqualifiedly exported - Unqualified, - /// Package is qualifiedly exported - AllUnnamed, - /// Package is exported to all unnamed modules - UnqualifiedOrAllUnnamed, -} - -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] -pub struct Package { - name: Box<[u1]>, - module: ModuleRef, - - export_type: PackageExportType, -} diff --git a/runtime/src/thread/java_lang_Thread.rs b/runtime/src/thread/java_lang_Thread.rs index ec65d02..ca751ee 100644 --- a/runtime/src/thread/java_lang_Thread.rs +++ b/runtime/src/thread/java_lang_Thread.rs @@ -5,7 +5,6 @@ use crate::objects::reference::Reference; use common::traits::PtrType; use instructions::Operand; -use symbols::sym; /// Value for the `java.lang.Thread$FieldHolder#status` field #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -21,7 +20,6 @@ pub enum ThreadStatus { BlockedOnMonitorEnter = 7, Terminated = 8, } - pub(super) fn set_eetop(obj: Reference, eetop: jni::sys::jlong) { let offset = crate::globals::fields::java_lang_Thread::eetop_field_offset(); @@ -41,7 +39,6 @@ pub mod holder { use common::traits::PtrType; use instructions::Operand; use jni::sys::jlong; - fn get_field_holder_field(obj: &Reference, offset: usize) -> Operand { let class_instance = obj.extract_class(); diff --git a/runtime/src/thread/mod.rs b/runtime/src/thread/mod.rs index 5f057a1..c532348 100644 --- a/runtime/src/thread/mod.rs +++ b/runtime/src/thread/mod.rs @@ -359,7 +359,16 @@ impl JavaThread { self.frame_stack .push(StackFrame::Native(NativeFrame { method })); - let ret = fn_ptr(self.env(), locals); + // TODO: Exception check + let ret; + if method.is_static() { + let fn_ptr = unsafe { fn_ptr.as_static() }; + ret = fn_ptr(self.env(), method.class(), locals); + } else { + let fn_ptr = unsafe { fn_ptr.as_non_static() }; + ret = fn_ptr(self.env(), locals); + } + assert!( self.frame_stack.pop_native().is_some(), "native frame consumed" diff --git a/symbols/src/lib.rs b/symbols/src/lib.rs index edfa974..cf46952 100644 --- a/symbols/src/lib.rs +++ b/symbols/src/lib.rs @@ -217,6 +217,9 @@ vm_symbols::define_symbols! { run_name: "run", // -- GENERATED METHOD NAME MARKER, DO NOT DELETE -- + // Modules + java_base: "java.base", + // Fields ADDRESS_SIZE0: "ADDRESS_SIZE0", PAGE_SIZE: "PAGE_SIZE", @@ -224,6 +227,8 @@ vm_symbols::define_symbols! { UNALIGNED_ACCESS: "UNALIGNED_ACCESS", DATA_CACHE_LINE_FLUSH_SIZE: "DATA_CACHE_LINE_FLUSH_SIZE", name: "name", + module: "module", + classLoader: "classLoader", referent: "referent", loader: "loader", holder: "holder",