diff --git a/.cargo/config.toml b/.cargo/config.toml index e56cdfb..9823a4d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,61 +1,2 @@ -[target.'cfg(all())'] -rustflags = [ - #### DOES NOT CHANGE #### - - # Forbids - "-Fclippy::dbg_macro", - "-Fclippy::string_to_string", - - # Denies - "-Dclippy::pedantic", - "-Dclippy::all", - "-Drust_2018_idioms", - "-Dtrivial_casts", - "-Dtrivial_numeric_casts", - "-Dunused_import_braces", - "-Dexplicit_outlives_requirements", - - # Allows - "-Aunknown_lints", - "-Aclippy::too_many_lines", - "-Aclippy::cast_precision_loss", - "-Aclippy::cast_sign_loss", - "-Aclippy::cast_possible_wrap", - "-Aclippy::cast_possible_truncation", - "-Aclippy::module_name_repetitions", - "-Aclippy::must_use_candidate", - "-Alet_underscore_drop", - "-Aclippy::match_wildcard_for_single_variants", - "-Aclippy::semicolon_if_nothing_returned", - "-Aclippy::new_without_default", - "-Aclippy::from_over_into", - "-Aclippy::upper_case_acronyms", - "-Aclippy::single_match_else", - "-Aclippy::similar_names", - "-Aclippy::len_without_is_empty", - "-Aclippy::needless_late_init", - "-Aclippy::type_complexity", - "-Aclippy::type_repetition_in_bounds", - "-Aunused_qualifications", - "-Aclippy::return_self_not_must_use", - "-Aclippy::bool_to_int_with_if", - "-Aclippy::uninlined_format_args", - "-Aclippy::manual_let_else", - - # For Library docs - # "-Dmissing_docs", - # "-Drustdoc::broken_intra_doc_links", - # "-Aclippy::doc_markdown", - # "-Aclippy::tabs_in_doc_comments", - - #### EXTRAS BELOW #### - "-Aclippy::inline_always", - "-Aclippy::new-ret-no-self", - "-Aclippy::mut-from-ref", - "-Aclippy::used_underscore_binding", - "-Aclippy::needless_pass_by_value", - "-Aclippy::manual_assert", - "-Aclippy::unnecessary_wraps", - # TODO: Remove this - "-Aclippy::missing-panics-doc", -] \ No newline at end of file +[build] +target-dir = "target" \ No newline at end of file diff --git a/classfile/src/accessflags.rs b/classfile/src/accessflags.rs index 4ffc498..6bc9149 100644 --- a/classfile/src/accessflags.rs +++ b/classfile/src/accessflags.rs @@ -175,7 +175,7 @@ impl_is_methods! { impl MethodAccessFlags { is_public => ACC_PUBLIC; is_private => ACC_PRIVATE; - is_protected => ACC_PRIVATE; + is_protected => ACC_PROTECTED; is_static => ACC_STATIC; is_final => ACC_FINAL; is_synchronized => ACC_SYNCHRONIZED; @@ -223,7 +223,7 @@ impl_is_methods! { impl FieldAccessFlags { is_public => ACC_PUBLIC; is_private => ACC_PRIVATE; - is_protected => ACC_PRIVATE; + is_protected => ACC_PROTECTED; is_static => ACC_STATIC; is_final => ACC_FINAL; is_volatile => ACC_VOLATILE; diff --git a/generators/native_methods/src/definitions.rs b/generators/native_methods/src/definitions.rs index 306b3f7..bd03b3b 100644 --- a/generators/native_methods/src/definitions.rs +++ b/generators/native_methods/src/definitions.rs @@ -51,16 +51,15 @@ pub fn generate_definitions_for_class(def_path: &Path, class: &Class) { macro_rules! non_static_signature { () => { - "\tpub fn _{}(env: std::ptr::NonNull<::jni::env::JniEnv>, locals: \ - crate::stack::local_stack::LocalStack) -> crate::native::method::NativeReturn {{" + "\tpub fn _{}(env: ::jni::env::JniEnv, locals: crate::stack::local_stack::LocalStack) -> \ + crate::native::method::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::method::NativeReturn {{" + "\tpub fn _{}(env: ::jni::env::JniEnv, class: &'static crate::objects::class::Class, \ + locals: crate::stack::local_stack::LocalStack) -> crate::native::method::NativeReturn {{" }; } diff --git a/generators/native_methods/src/field.rs b/generators/native_methods/src/field.rs index 5dc75c5..38b2454 100644 --- a/generators/native_methods/src/field.rs +++ b/generators/native_methods/src/field.rs @@ -52,7 +52,7 @@ fn write_entries_for_class(mut file: Option, def_path: &Path, class: &Clas Member::Field(field) => { writeln!( file.as_mut().expect("file should exist"), - "#[allow(non_upper_case_globals)]\npub const {}: {} = {};", + "#[allow(non_upper_case_globals, dead_code)]\npub const {}: {} = {};", field.name, field.ty.map_to_rust_ty(), field.expr diff --git a/generators/native_methods/src/registernatives.rs b/generators/native_methods/src/registernatives.rs index 052a4da..383d1e3 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, _: &'static crate::objects::class::Class) {{ +pub fn registerNatives(_: ::jni::env::JniEnv, _: &'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) {{ @@ -59,7 +59,7 @@ pub(crate) fn generate_register_natives_table( ) .unwrap(); - for ref member in class.members.extract_if(|member| { + for ref member in class.members.extract_if(.., |member| { matches!(member, Member::Method(method) if method.name() != "registerNatives" && method.modifiers.contains(AccessFlags::ACC_NATIVE) && !method.modifiers.contains(AccessFlags::ACC_STATIC)) }).collect::>() { match member { diff --git a/jimage/src/decompressor.rs b/jimage/src/decompressor.rs index aaf9701..c8e6303 100644 --- a/jimage/src/decompressor.rs +++ b/jimage/src/decompressor.rs @@ -1,11 +1,11 @@ +use crate::error::{Error, Result}; use crate::ImageStrings; use std::io::Write; -use std::ptr::read_unaligned as ptread; -use common::box_slice; use common::endian::Endian; use common::int_types::{u1, u4, u8}; +use common::traits::JavaEndianAwareRead; pub struct ResourceHeader { pub(crate) __magic: u4, @@ -20,85 +20,29 @@ impl ResourceHeader { pub const RESOURCE_HEADER_MAGIC: u4 = 0xCAFE_FAFA; } -unsafe fn get_u8(ptr: *mut u1, endian: Endian) -> u8 { - match endian { - Endian::Little => { - u8::from(ptread::(ptr)) - | u8::from(ptread::(ptr.add(1))) << 8 - | u8::from(ptread::(ptr.add(2))) << 16 - | u8::from(ptread::(ptr.add(3))) << 24 - | u8::from(ptread::(ptr.add(4))) << 32 - | u8::from(ptread::(ptr.add(5))) << 40 - | u8::from(ptread::(ptr.add(6))) << 48 - | u8::from(ptread::(ptr.add(7))) << 56 - }, - Endian::Big => { - u8::from(ptread::(ptr)) << 56 - | u8::from(ptread::(ptr.add(1))) << 48 - | u8::from(ptread::(ptr.add(2))) << 40 - | u8::from(ptread::(ptr.add(3))) << 32 - | u8::from(ptread::(ptr.add(4))) << 24 - | u8::from(ptread::(ptr.add(5))) << 16 - | u8::from(ptread::(ptr.add(6))) << 8 - | u8::from(ptread::(ptr.add(7))) - }, - } -} - -unsafe fn get_u4(ptr: *mut u1, endian: Endian) -> u4 { - match endian { - Endian::Little => { - u4::from(ptread::(ptr)) - | u4::from(ptread::(ptr.add(1))) << 8 - | u4::from(ptread::(ptr.add(2))) << 16 - | u4::from(ptread::(ptr.add(3))) << 24 - }, - Endian::Big => { - u4::from(ptread::(ptr)) << 24 - | u4::from(ptread::(ptr.add(1))) << 16 - | u4::from(ptread::(ptr.add(2))) << 8 - | u4::from(ptread::(ptr.add(3))) - }, - } -} - // https://github.com/openjdk/jdk/blob/f80faced6e6c6c1b10541a8b0c91625215c9ef43/src/java.base/share/native/libjimage/imageDecompressor.cpp#L136 /// Decompression entry point. Called from [`ImageFileReader::get_resource`]. #[allow(clippy::size_of_in_element_count)] pub fn decompress_resource( - compressed: &mut [u1], + compressed: &mut &[u1], mut uncompressed: &mut [u1], uncompressed_size: u8, strings: ImageStrings<'_>, endian: Endian, -) { +) -> Result<()> { let mut has_header; - let mut resource = compressed.as_mut_ptr(); + let mut resource = compressed.as_ptr(); // Resource could have been transformed by a stack of decompressors. // Iterate and decompress resources until there is no more header. loop { - let magic; - let size; - let uncompressed_size; - let decompressor_name_offset; - let decompressor_config_offset; - let is_terminal; - unsafe { - magic = get_u4(resource, endian); - resource = resource.add(core::mem::size_of::()); - size = get_u8(resource, endian); - resource = resource.add(core::mem::size_of::()); - uncompressed_size = get_u8(resource, endian); - resource = resource.add(core::mem::size_of::()); - decompressor_name_offset = get_u4(resource, endian); - resource = resource.add(core::mem::size_of::()); - decompressor_config_offset = get_u4(resource, endian); - resource = resource.add(core::mem::size_of::()); - is_terminal = ptread::(resource); - resource = resource.add(core::mem::size_of::()); - } + let magic = endian.read_u4(compressed)?; + let size = endian.read_u8(compressed)?; + let uncompressed_size = endian.read_u8(compressed)?; + let decompressor_name_offset = endian.read_u4(compressed)?; + let decompressor_config_offset = endian.read_u4(compressed)?; + let is_terminal = endian.read_u1(compressed)?; let header = ResourceHeader { __magic: magic, @@ -109,39 +53,38 @@ pub fn decompress_resource( is_terminal, }; + resource = unsafe { resource.add(size_of::()) }; + has_header = header.__magic == ResourceHeader::RESOURCE_HEADER_MAGIC; if !has_header { resource = unsafe { resource.sub(size_of::()) }; break; } - // decompressed_resource array contains the result of decompression - let decompressed_resource = box_slice![0; header.uncompressed_size as usize]; - - // We need to reconstruct our box and drop it in the next iteration - let decompressed_resource = Box::leak(decompressed_resource); - // Retrieve the decompressor name let decompressor_name = strings.get(header.decompressor_name_offset); - assert!( - !decompressor_name.is_empty(), - "image decompressor not found" - ); // Retrieve the decompressor instance // Ask the decompressor to decompress the compressed content + let decompressed_resource; match decompressor_name { - b"zip" => decompress_zip(resource, decompressed_resource, &header, strings), - b"compact-cp" => decompress_string(resource, decompressed_resource, &header, strings), - _ => panic!( - "image decompressor not found: {}", - std::str::from_utf8(decompressor_name).unwrap() - ), + b"zip" => decompressed_resource = decompress_zip(compressed, &header, strings), + b"compact-cp" => { + decompressed_resource = decompress_string(compressed, &header, strings) + }, + _ => { + return Err(Error::DecompressorNotFound( + String::from_utf8_lossy(decompressor_name).into_owned(), + )) + }, } + // We need to reconstruct our box and drop it in the next iteration + let decompressed_resource = Box::leak(decompressed_resource); + // Drop the previous iteration's decompressed contents unsafe { - let _ = Box::from_raw(resource); + let _ = Box::from_raw(resource as *mut u1); } // Preserve this iteration's decompressed contents for the next round @@ -149,27 +92,25 @@ pub fn decompress_resource( } // Now we can write the resource to our uncompressed buffer - uncompressed - .write_all(unsafe { - std::slice::from_raw_parts(resource, uncompressed_size.try_into().unwrap()) - }) - .unwrap(); + uncompressed.write_all(unsafe { + std::slice::from_raw_parts(resource, uncompressed_size.try_into().unwrap()) + })?; + + Ok(()) } fn decompress_zip( - _compressed: *mut u1, - _uncompressed: &mut [u1], + _compressed: &mut &[u1], _header: &ResourceHeader, _strings: ImageStrings<'_>, -) { +) -> Box<[u1]> { unimplemented!("zip decompression") } fn decompress_string( - _compressed: *mut u1, - _uncompressed: &mut [u1], + _compressed: &mut &[u1], _header: &ResourceHeader, _strings: ImageStrings<'_>, -) { +) -> Box<[u1]> { unimplemented!("string decompression") } diff --git a/jimage/src/error.rs b/jimage/src/error.rs index 3d80755..99891f2 100644 --- a/jimage/src/error.rs +++ b/jimage/src/error.rs @@ -7,6 +7,7 @@ pub enum Error { InvalidMagic, InvalidTableSize, BadIndexSize, + DecompressorNotFound(String), Common(common::error::CommonError), Io(std::io::Error), @@ -22,6 +23,7 @@ impl Display for Error { f, "The index does not match the size provided in the header" ), + Self::DecompressorNotFound(s) => write!(f, "Image decompressor \"{s}\" not found"), Self::Common(err) => write!(f, "{}", err), Self::Io(err) => write!(f, "{}", err), diff --git a/jimage/src/image.rs b/jimage/src/image.rs index d3c0029..f172142 100644 --- a/jimage/src/image.rs +++ b/jimage/src/image.rs @@ -62,13 +62,15 @@ impl JImage { // https://github.com/openjdk/jdk/blob/f56285c3613bb127e22f544bd4b461a0584e9d2a/src/java.base/share/native/libjimage/imageFile.cpp#L523 /// Return the resource for the supplied location offset. - pub fn get_resource(&self, offset: u4, uncompressed_data: &mut [u1]) { + pub fn get_resource(&self, offset: u4, uncompressed_data: &mut [u1]) -> Result<()> { // Get address of first byte of location attribute stream. let data = self.get_location_offset_data(offset); // Expand location attributes. let location = JImageLocation::new_opt_(self, data); // Read the data - self.get_resource_from_location(&location, uncompressed_data); + self.get_resource_from_location(&location, uncompressed_data)?; + + Ok(()) } // https://github.com/openjdk/jdk/blob/f56285c3613bb127e22f544bd4b461a0584e9d2a/src/java.base/share/native/libjimage/imageFile.cpp#L533 @@ -77,7 +79,7 @@ impl JImage { &self, location: &JImageLocation<'_>, uncompressed_data: &mut [u1], - ) { + ) -> Result<()> { // Retrieve the byte offset and size of the resource. let offset = location.get_content_offset() as usize; let uncompressed_size = location.get_uncompressed_size(); @@ -91,12 +93,11 @@ impl JImage { (&mut data).read_exact(uncompressed_data).is_ok(), "error reading from image or short read" ); - return; + return Ok(()); } // We have to decompress the data - let mut compressed_data = - Box::<[u1]>::from(&self.resources[offset..offset + compressed_size as usize]); + let mut compressed_data = &self.resources[offset..offset + compressed_size as usize]; // Get image string table. let strings = ImageStrings(self.index.string_bytes()); // Decompress resource. @@ -106,7 +107,9 @@ impl JImage { uncompressed_size, strings, self.endian, - ); + )?; + + Ok(()) } /// Return a sorted collection of all paths to valid locations @@ -203,7 +206,7 @@ impl JImage { fn verify_location(&self, location: &JImageLocation<'_>, path: &str) -> bool { // Manage the image string table. let strings = ImageStrings(self.index.string_bytes()); - + // Get module name string. let module = location.get_attribute_string(crate::location::attr::ATTRIBUTE_MODULE as u4, strings); diff --git a/jni/src/env/array.rs b/jni/src/env/array.rs index 00c8f89..04e0260 100644 --- a/jni/src/env/array.rs +++ b/jni/src/env/array.rs @@ -27,20 +27,17 @@ impl super::JniEnv { init: Option, ) -> Result { let ret; - let exception; unsafe { let invoke_interface = self.as_native_interface(); ret = ((*invoke_interface).NewObjectArray)( - self.0 as _, + self.raw(), len, class.raw(), init.map_or(core::ptr::null_mut(), |init| init.raw()), ); - - exception = ((*invoke_interface).ExceptionCheck)(self.0 as _); } - if exception { + if self.exception_check() { return Err(JniError::ExceptionThrown); } @@ -73,7 +70,6 @@ impl super::JniEnv { index: jsize, val: Option>, ) -> Result<()> { - let exception; unsafe { let invoke_interface = self.as_native_interface(); ((*invoke_interface).SetObjectArrayElement)( @@ -82,11 +78,9 @@ impl super::JniEnv { index, val.map_or(core::ptr::null_mut(), |init| init.into().raw()), ); - - exception = ((*invoke_interface).ExceptionCheck)(self.0 as _); } - if exception { + if self.exception_check() { return Err(JniError::ExceptionThrown); } diff --git a/jni/src/env/class.rs b/jni/src/env/class.rs index 597ead5..eba7907 100644 --- a/jni/src/env/class.rs +++ b/jni/src/env/class.rs @@ -26,15 +26,12 @@ impl super::JniEnv { let name = name.into(); let ret; - let exception; unsafe { let invoke_interface = self.as_native_interface(); ret = ((*invoke_interface).FindClass)(self.0 as _, name.as_cstr().as_ptr()); - - exception = ((*invoke_interface).ExceptionCheck)(self.0 as _); } - if exception { + if self.exception_check() { return Err(JniError::ExceptionThrown); } diff --git a/jni/src/env/exceptions.rs b/jni/src/env/exceptions.rs index b63e719..5611be3 100644 --- a/jni/src/env/exceptions.rs +++ b/jni/src/env/exceptions.rs @@ -1,9 +1,28 @@ impl super::JniEnv { - // TODO: Throw - // TODO: ThrowNew - // TODO: ExceptionOccurred - // TODO: ExceptionDescribe - // TODO: ExceptionClear - // TODO: FatalError - // TODO: ExceptionCheck + // TODO: Throw + // TODO: ThrowNew + // TODO: ExceptionOccurred + + /// Prints an exception and a backtrace of the stack to a system error-reporting channel, such as stderr. + /// + /// This is a convenience routine provided for debugging. + pub fn exception_describe(&self) { + unsafe { + let invoke_interface = self.as_native_interface(); + ((*invoke_interface).ExceptionDescribe)(self.0 as _); + } + } + // TODO: ExceptionClear + // TODO: FatalError + + /// Returns `true` when there is a pending exception + pub fn exception_check(&self) -> bool { + let ret; + unsafe { + let invoke_interface = self.as_native_interface(); + ret = ((*invoke_interface).ExceptionCheck)(self.0 as _); + } + + ret + } } diff --git a/jni/src/env/method.rs b/jni/src/env/method.rs index bf0d04a..0619306 100644 --- a/jni/src/env/method.rs +++ b/jni/src/env/method.rs @@ -105,7 +105,6 @@ impl super::JniEnv { let sig = sig.into(); let ret; - let exception; unsafe { let invoke_interface = self.as_native_interface(); ret = ((*invoke_interface).GetStaticMethodID)( @@ -114,11 +113,9 @@ impl super::JniEnv { name.as_cstr().as_ptr(), sig.as_cstr().as_ptr(), ); - - exception = ((*invoke_interface).ExceptionCheck)(self.0 as _); } - if exception { + if self.exception_check() { return Err(JniError::ExceptionThrown); } @@ -167,7 +164,6 @@ impl super::JniEnv { .map(JValue::raw) .collect::>(); - let exception; unsafe { let invoke_interface = self.as_native_interface(); ((*invoke_interface).CallStaticVoidMethod)( @@ -176,11 +172,9 @@ impl super::JniEnv { method_id.raw(), new_args.as_ptr(), ); - - exception = ((*invoke_interface).ExceptionCheck)(self.0 as _); } - if exception { + if self.exception_check() { return Err(JniError::ExceptionThrown); } diff --git a/jni/src/env/mod.rs b/jni/src/env/mod.rs index b0f3c32..4cd99b2 100644 --- a/jni/src/env/mod.rs +++ b/jni/src/env/mod.rs @@ -17,18 +17,21 @@ mod weak; /// Safer wrapper around `jni_sys::JNIEnv` #[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq)] -pub struct JniEnv(jni_sys::JNIEnv); +pub struct JniEnv(*mut jni_sys::JNIEnv); impl JniEnv { - pub fn raw(&self) -> jni_sys::JNIEnv { + pub fn raw(&self) -> *mut jni_sys::JNIEnv { self.0 } - pub unsafe fn from_raw(env: jni_sys::JNIEnv) -> Self { + pub unsafe fn from_raw(env: *mut jni_sys::JNIEnv) -> Self { Self(env) } unsafe fn as_native_interface(&self) -> *const jni_sys::JNINativeInterface_ { - self.0 as _ + assert!(!self.0.is_null()); + + // Assuming this was created using the safe APIs, the pointer will always be valid + unsafe { (*self.0) as _ } } } diff --git a/jni/src/env/string.rs b/jni/src/env/string.rs index 61d26af..68a17d3 100644 --- a/jni/src/env/string.rs +++ b/jni/src/env/string.rs @@ -25,15 +25,12 @@ impl super::JniEnv { let utf = utf.into(); let ret; - let exception; unsafe { let invoke_interface = self.as_native_interface(); ret = ((*invoke_interface).NewStringUTF)(self.0 as _, utf.as_cstr().as_ptr()); - - exception = ((*invoke_interface).ExceptionCheck)(self.0 as _); } - if exception { + if self.exception_check() { return Err(JniError::ExceptionThrown); } diff --git a/jni/src/java_vm.rs b/jni/src/java_vm.rs index e601bf6..14f7950 100644 --- a/jni/src/java_vm.rs +++ b/jni/src/java_vm.rs @@ -9,9 +9,10 @@ pub use attach_args::*; pub use error::*; pub use init_args::*; -use core::ffi::c_void; use std::path::PathBuf; +use jni_sys::JNIEnv; + #[derive(Default, Debug, Clone)] pub struct JavaVmBuilder { jvm_lib_path: Option, @@ -20,7 +21,7 @@ pub struct JavaVmBuilder { type CreateJavaVmFn = unsafe extern "system" fn( *mut *mut jni_sys::JavaVM, - *mut *mut c_void, + *mut *mut (), *mut jni_sys::JavaVMInitArgs, ) -> jni_sys::jint; @@ -49,9 +50,7 @@ impl JavaVmBuilder { } pub fn build(self) -> Result<(JavaVm, JniEnv)> { - let libjvm_path = self - .jvm_lib_path - .unwrap_or_else(|| PathBuf::from("/home/alex/.cache/target/debug/libjvm_runtime.so")); + let libjvm_path = self.jvm_lib_path.unwrap_or_else(default_libjvm_path); let args = self.args.unwrap_or_default().finish(); let ret; @@ -77,12 +76,32 @@ impl JavaVmBuilder { } let java_vm = unsafe { JavaVm::from_raw(javavm_raw) }; - let jni_env = unsafe { JniEnv::from_raw(jni_env_raw as _) }; + let jni_env = unsafe { JniEnv::from_raw(jni_env_raw as *mut JNIEnv) }; Ok((java_vm, jni_env)) } } +#[cfg(debug_assertions)] +fn default_libjvm_path() -> PathBuf { + let target = std::env::var("CARGO_TARGET_DIR").map_or_else( + |_| { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .join("target") + }, + PathBuf::from, + ); + + target.join("debug").join("libjvm_runtime.so") +} + +#[cfg(not(debug_assertions))] +fn default_libjvm_path() -> PathBuf { + todo!("Determine libjvm path") +} + /// A wrapper around a built [`jni_sys::JavaVM`] /// /// See [`JavaVmBuilder`]. diff --git a/jni/src/string.rs b/jni/src/string.rs index 8aff72b..f63dc58 100644 --- a/jni/src/string.rs +++ b/jni/src/string.rs @@ -28,7 +28,9 @@ where T: AsRef, { fn from(value: T) -> Self { - let encoded = cesu8::to_java_cesu8(value.as_ref()).into_owned(); + let mut encoded = cesu8::to_java_cesu8(value.as_ref()).into_owned(); + encoded.push(b'\0'); + JCesu8String { inner: unsafe { CString::from_vec_with_nul_unchecked(encoded) }, } diff --git a/runtime/src/classpath/jimage.rs b/runtime/src/classpath/jimage.rs index 4712396..b5a7fd1 100644 --- a/runtime/src/classpath/jimage.rs +++ b/runtime/src/classpath/jimage.rs @@ -15,7 +15,8 @@ pub fn lookup_vm_resource(path: &str) -> Option> { if let Some(file) = unsafe { &*JIMAGE_FILE.get() } { if let Some((location_offset, size)) = file.find_resource("java.base", path) { let mut uncompressed_data = vec![0; size as usize]; - file.get_resource(location_offset, &mut uncompressed_data); + file.get_resource(location_offset, &mut uncompressed_data) + .unwrap(); // TODO: Error handling return Some(uncompressed_data); } @@ -25,10 +26,7 @@ pub fn lookup_vm_resource(path: &str) -> Option> { } pub fn lookup_vm_options() -> Option> { - assert!( - unsafe { (*JIMAGE_FILE.get()).is_none() }, - "Attempt to lookup vm options twice!" - ); + assert!(!initialized(), "Attempt to lookup vm options twice!"); let java_home = std::env::var("JAVA_HOME").expect("JAVA_HOME not set"); diff --git a/runtime/src/classpath/classloader.rs b/runtime/src/classpath/loader/mod.rs similarity index 79% rename from runtime/src/classpath/classloader.rs rename to runtime/src/classpath/loader/mod.rs index c35247b..d22806c 100644 --- a/runtime/src/classpath/classloader.rs +++ b/runtime/src/classpath/loader/mod.rs @@ -1,12 +1,17 @@ -use crate::modules::{Module, ModuleLockGuard, Package}; +mod set; +pub use set::*; + +use crate::modules::{Module, ModuleLockGuard, ModuleSet, Package}; use crate::objects::class::Class; +use crate::objects::instance::Instance; use crate::objects::reference::Reference; +use crate::string_interner::StringInterner; use std::cell::SyncUnsafeCell; use std::collections::HashMap; use std::fmt::Debug; use std::ops::RangeInclusive; -use std::sync::{Arc, LazyLock, Mutex}; +use std::sync::{LazyLock, Mutex}; use classfile::{ClassFile, FieldType}; use common::int_types::u1; @@ -18,28 +23,23 @@ const SUPPORTED_MAJOR_UPPER_BOUND: u1 = 69; const SUPPORTED_MAJOR_VERSION_RANGE: RangeInclusive = SUPPORTED_MAJOR_LOWER_BOUND..=SUPPORTED_MAJOR_UPPER_BOUND; -#[derive(Copy, Clone, Debug)] -struct ClassLoaderFlags { - is_bootstrap: bool, -} - pub struct ClassLoader { - flags: ClassLoaderFlags, obj: Reference, - name: Symbol, + name: Option, + name_and_id: Symbol, - unnamed_module: SyncUnsafeCell>>, + unnamed_module: SyncUnsafeCell>, classes: Mutex>, // 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>>, + java_base: SyncUnsafeCell>, // Access to these fields is manually synchronized with the global module mutex - modules: SyncUnsafeCell>>, + modules: ModuleSet, packages: SyncUnsafeCell>, } @@ -52,19 +52,72 @@ impl PartialEq for ClassLoader { 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 fn from_obj(obj: Reference) -> Option<&'static Self> { - if obj.is_null() { - return Some(Self::bootstrap()); + fn from_obj(obj: Reference) -> Self { + assert!(!obj.is_null(), "cannot create ClassLoader from null obj"); + assert!(obj.is_instance_of(crate::globals::classes::java_lang_ClassLoader())); + + let mut name = None; + + let name_obj = obj + .get_field_value0(crate::globals::fields::java_lang_ClassLoader::name_field_offset()) + .expect_reference(); + if !name_obj.is_null() { + let name_str = StringInterner::rust_string_from_java_string(name_obj.extract_class()); + + if !name_str.is_empty() { + name = Some(Symbol::intern_owned(name_str)); + } + } + + let unnamed_module_obj = obj + .get_field_value0( + crate::globals::fields::java_lang_ClassLoader::unnamedModule_field_offset(), + ) + .expect_reference(); + assert!( + !unnamed_module_obj.is_null() + && unnamed_module_obj.is_instance_of(crate::globals::classes::java_lang_Module()) + ); + + let unnamed_module = + Module::unnamed(unnamed_module_obj).expect("unnamed module creation failed"); + + let name_and_id_obj = obj + .get_field_value0( + crate::globals::fields::java_lang_ClassLoader::nameAndId_field_offset(), + ) + .expect_reference(); + + let name_and_id; + if name_and_id_obj.is_null() { + name_and_id = obj.extract_target_class().name.as_str().replace('/', "."); + } else { + name_and_id = + StringInterner::rust_string_from_java_string(name_and_id_obj.extract_class()); } - todo!("Non-bootstrap classloaders") + assert!(!name_and_id.is_empty(), "class loader has no name and id"); + + Self { + obj, + name, + name_and_id: Symbol::intern_owned(name_and_id), + + unnamed_module: SyncUnsafeCell::new(Some(Box::leak(Box::new(unnamed_module)))), + classes: Mutex::new(HashMap::new()), + + // Never initialized for non-bootstrap loaders + java_base: SyncUnsafeCell::new(None), + + modules: ModuleSet::new(), + packages: SyncUnsafeCell::new(HashMap::new()), + } } } @@ -80,22 +133,16 @@ impl ClassLoader { packages.get(&name) } - pub fn insert_module(&self, _guard: &ModuleLockGuard, module: Module) -> Arc { - let Some(module_name) = module.name() else { + pub fn insert_module(&self, _guard: &ModuleLockGuard, module: Module) -> &'static Module { + if module.name().is_none() { panic!("Attempted to insert an unnamed module using `insert_module`") }; - let modules = unsafe { &mut *self.modules.get() }; - Arc::clone( - modules - .entry(module_name) - .or_insert_with(|| Arc::new(module)), - ) + self.modules.add(_guard, module) } - pub fn lookup_module(&self, _guard: &ModuleLockGuard, name: Symbol) -> Option> { - let modules = unsafe { &*self.modules.get() }; - modules.get(&name).map(Arc::clone) + pub fn lookup_module(&self, _guard: &ModuleLockGuard, name: Symbol) -> Option<&'static Module> { + self.modules.find(_guard, name) } } @@ -103,17 +150,19 @@ impl ClassLoader { impl ClassLoader { pub fn bootstrap() -> &'static Self { static BOOTSTRAP_LOADER: LazyLock> = LazyLock::new(|| { + let name_sym = Symbol::intern("bootstrap"); + let loader = ClassLoader { - flags: ClassLoaderFlags { is_bootstrap: true }, obj: Reference::null(), - name: Symbol::intern("bootstrap"), + name: Some(name_sym), + name_and_id: name_sym, unnamed_module: SyncUnsafeCell::new(None), classes: Mutex::new(HashMap::new()), java_base: SyncUnsafeCell::new(None), - modules: SyncUnsafeCell::new(HashMap::new()), + modules: ModuleSet::new(), packages: SyncUnsafeCell::new(HashMap::new()), }; @@ -123,12 +172,9 @@ impl ClassLoader { unsafe { &*BOOTSTRAP_LOADER.get() } } - pub fn java_base(&self) -> Arc { + pub fn java_base(&self) -> &'static Module { assert!(self.is_bootstrap()); - unsafe { &*self.java_base.get() } - .as_ref() - .map(Arc::clone) - .expect("java.base should be set") + unsafe { &*self.java_base.get() }.expect("java.base should be set") } pub fn set_java_base(&self, java_base: Module) { @@ -138,11 +184,11 @@ impl ClassLoader { "java.base cannot be set more than once" ); - unsafe { *ptr = Some(Arc::new(java_base)) } + unsafe { *ptr = Some(Box::leak(Box::new(java_base))) } } pub fn is_bootstrap(&self) -> bool { - self.flags.is_bootstrap + self.obj.is_null() } /// Stores a copy of the `jdk.internal.loader.BootLoader#UNNAMED_MODULE` field @@ -159,7 +205,7 @@ impl ClassLoader { ); unsafe { - *ptr = Some(Arc::new(entry)); + *ptr = Some(Box::leak(Box::new(entry))); } } } @@ -170,25 +216,35 @@ impl ClassLoader { loaded_classes.get(&name).map(|&class| class) } - pub fn unnamed_module(&self) -> Arc { - unsafe { &*self.unnamed_module.get() } - .as_ref() - .map(Arc::clone) - .expect("unnamed module should be set") + pub fn unnamed_module(&self) -> &'static Module { + unsafe { &*self.unnamed_module.get() }.expect("unnamed module should be set") } } impl ClassLoader { - pub fn name(&self) -> Symbol { + /// Get the `name` field of the `ClassLoader`, if it has been set + pub fn name(&self) -> Option { self.name } + /// Get the `nameAndId` field of the `ClassLoader` + /// + /// Unlike [`name`], this field is always available. + /// + /// The format is: + /// * The loader has a name defined: `'' @` + /// * The loader has no name defined: ` @` + /// * The loader is built-in: `@` is omitted, as there is only one instance + pub fn name_and_id(&self) -> Symbol { + self.name_and_id + } + pub fn obj(&self) -> Reference { self.obj.clone() } pub fn load(&'static self, name: Symbol) -> Option<&'static Class> { - if self.flags.is_bootstrap { + if self.is_bootstrap() { return self.load_bootstrap(name); } diff --git a/runtime/src/classpath/loader/set.rs b/runtime/src/classpath/loader/set.rs new file mode 100644 index 0000000..34d3b2f --- /dev/null +++ b/runtime/src/classpath/loader/set.rs @@ -0,0 +1,72 @@ +use super::ClassLoader; +use crate::objects::instance::Instance; +use crate::objects::reference::Reference; + +use std::cell::SyncUnsafeCell; +use std::collections::LinkedList; +use std::sync::{LazyLock, Mutex}; + +use common::traits::PtrType; +use instructions::Operand; +use jni::sys::jlong; + +pub struct ClassLoaderSet { + // A list of all currently alive classloaders. + // + // This is a `LinkedList`, as reads will do a lifetime-extension, and are not guarded. We cannot + // risk a realloc invalidating a reference. + list: SyncUnsafeCell>, + write_mutex: Mutex<()>, +} + +static CLASS_LOADER_SET: LazyLock = LazyLock::new(|| ClassLoaderSet { + list: SyncUnsafeCell::new(LinkedList::new()), + write_mutex: Mutex::new(()), +}); + +impl ClassLoaderSet { + pub fn add(loader: Reference) -> &'static ClassLoader { + let _guard = CLASS_LOADER_SET.write_mutex.lock().unwrap(); + + let class_loader = ClassLoader::from_obj(loader.clone()); + + let list = unsafe { &mut *CLASS_LOADER_SET.list.get() }; + list.push_back(class_loader); + + let ret = list.back().unwrap(); + + // Store the pointer in the classloader, to make future lookups cheaper + loader.extract_class().get_mut().put_field_value0( + crate::globals::fields::java_lang_ClassLoader::loader_ptr_field_offset(), + Operand::Long(ret as *const ClassLoader as jlong), + ); + + ret + } + + pub fn find(loader: Reference) -> Option<&'static ClassLoader> { + if loader.is_null() { + return Some(ClassLoader::bootstrap()); + } + + let list = unsafe { &*CLASS_LOADER_SET.list.get() }; + list.iter().find(|cl| cl.obj == loader) + } + + pub fn find_or_add(loader: Reference) -> &'static ClassLoader { + if loader.is_null() { + return ClassLoader::bootstrap(); + } + + if let Some(class_loader_ptr) = + crate::globals::fields::java_lang_ClassLoader::injected_loader_ptr_for(loader.clone()) + { + return unsafe { &*class_loader_ptr }; + } + + match Self::find(loader.clone()) { + Some(cl) => cl, + None => Self::add(loader), + } + } +} diff --git a/runtime/src/classpath/mod.rs b/runtime/src/classpath/mod.rs index 1874cae..b9a6922 100644 --- a/runtime/src/classpath/mod.rs +++ b/runtime/src/classpath/mod.rs @@ -1,6 +1,6 @@ -pub mod classloader; pub mod jar; pub mod jimage; +pub mod loader; use std::fs::File; use std::io::Read; diff --git a/runtime/src/globals/fields.rs b/runtime/src/globals/fields.rs index d31faea..920eb9a 100644 --- a/runtime/src/globals/fields.rs +++ b/runtime/src/globals/fields.rs @@ -2,6 +2,8 @@ //! Various offsets for fields of frequently accessed classes +const MAX_FIELD_COUNT: usize = 8; + macro_rules! get_sym { ($specified_sym_name:ident $_fallback:ident) => { symbols::sym!($specified_sym_name) @@ -11,76 +13,202 @@ macro_rules! get_sym { }; } -// TODO: Document -macro_rules! field_module { +macro_rules! instance_field_count { + () => { + 0 + }; ( - @CLASS $class_name:ident; - $( $(#[$meta:meta])* $([sym: $specified_sym_name:ident])? - @FIELD $field_name:ident: $matcher:pat $(if $guard:expr)?, - )+ - $( - mod $inner_mod:ident { - $($inner_mod_tt:tt)* + @INJECTED $field_name:ident: $_descriptor:pat => $field_ty:ty, $($rest:tt)* + ) => { + 0 + instance_field_count!($($rest)*) + }; + ( + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @FIELD $field_name:ident: $matcher:pat $(if $guard:expr)?, $($rest:tt)* + ) => { + 1 + instance_field_count!($($rest)*) + }; +} + +macro_rules! injected_field_count { + () => { + 0 + }; + ( + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @INJECTED $field_name:ident: $_descriptor:expr => $field_ty:ty, $($rest:tt)* + ) => { + 1 + injected_field_count!($($rest)*) + }; + ( + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @FIELD $field_name:ident: $matcher:pat $(if $guard:expr)?, $($rest:tt)* + ) => { + 0 + injected_field_count!($($rest)*) + }; +} + +macro_rules! injected_field_definition { + ($class:ident, $($field_tt:tt)*) => { + injected_field_definition!(@ACC [] $class, $($field_tt)*) + }; + ( + @ACC [$($acc:tt)*] $class:ident, + + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @INJECTED $field_name:ident: $descriptor:expr => $_field_ty:ty, $($rest:tt)* + ) => { + injected_field_definition!(@ACC [$($acc)* crate::objects::field::Field::new_injected($class, get_sym!($($specified_sym_name)? $field_name), $descriptor), ] $class, $($rest)*) + }; + ( + @ACC [$($acc:tt)*] $class:ident, + + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @FIELD $field_name:ident: $matcher:pat $(if $guard:expr)?, $($rest:tt)* + ) => { + injected_field_definition!(@ACC [$($acc)*] $class, $($rest)*) + }; + (@ACC [$($acc:tt)*] $class:ident,) => { [$($acc)*] }; +} + +macro_rules! field_constructor { + ($class_name:ident $($sub_class_name:ident)? @FIELDSTART $($field_tt:tt)*) => { + field_constructor!(@METHODS $($field_tt)*); + + /// Initialize the field offsets + /// + /// # Safety + /// + /// This **requires** that the target class is loaded + pub unsafe fn init_offsets() { + const INSTANCE_FIELD_COUNT: usize = instance_field_count!($($field_tt)*); + const INJECTED_FIELD_COUNT: usize = injected_field_count!($($field_tt)*); + const EXPECTED_FIELD_SET: usize = (1 << INSTANCE_FIELD_COUNT) - 1; + const _: () = { + assert!(INSTANCE_FIELD_COUNT + INJECTED_FIELD_COUNT <= crate::globals::fields::MAX_FIELD_COUNT); + }; + + let class = crate::globals::classes::$class_name(); + + if INJECTED_FIELD_COUNT > 0 { + class.inject_fields( + injected_field_definition!(class, $($field_tt)*), + INJECTED_FIELD_COUNT + ); + } + + let mut field_set = 0; + for field in class.fields() { + field_constructor!(@CHECKS field, field_set, $($field_tt)*); + } + + assert_eq!(field_set, EXPECTED_FIELD_SET, "Not all fields found in {}", stringify!($class_name)); + + $( + unsafe { + $sub_class_name::init_offsets(); + } + )? } - )? + }; + (@METHODS + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @INJECTED $field_name:ident: $_descriptor:expr => $field_ty:ty, $($rest:tt)* + ) => { + // Treat this field as a normal field + field_constructor!(@METHODS + $(#[$meta])* + $([sym: $specified_sym_name])? + @FIELD $field_name: _, $($rest)* + ); + }; + (@METHODS + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @FIELD $field_name:ident: $matcher:pat $(if $guard:expr)?, $($rest:tt)* ) => { paste::paste! { - $( - static mut [<__ $field_name:snake:upper _FIELD_OFFSET>]: usize = 0; + static mut [<__ $field_name:snake:upper _FIELD_OFFSET>]: usize = 0; - $(#[$meta])* - /// This will not change for the lifetime of the program. - pub fn [<$field_name _field_offset>]() -> usize { - unsafe { [<__ $field_name:snake:upper _FIELD_OFFSET>] } - } + $(#[$meta])* + /// This will not change for the lifetime of the program. + pub fn [<$field_name _field_offset>]() -> usize { + unsafe { [<__ $field_name:snake:upper _FIELD_OFFSET>] } + } - unsafe fn [](value: usize) { - [<__ $field_name:snake:upper _FIELD_OFFSET>] = value; - } - )+ + unsafe fn [](value: usize) { + [<__ $field_name:snake:upper _FIELD_OFFSET>] = value; + } + } - /// Initialize the field offsets - /// - /// # Safety - /// - /// This **requires** that the target class is loaded - pub unsafe fn init_offsets() { - const EXPECTED_FIELD_SET: usize = (1 << ${count($field_name)}) - 1; - let class = crate::globals::classes::$class_name(); - - let mut field_set = 0; - for field in class.fields() { - $( - if field.name == get_sym!($($specified_sym_name)? $field_name) && matches!(&field.descriptor, $matcher $(if $guard)?) { - field_set |= 1 << ${index()}; - unsafe { [](field.idx); } - continue; - } - )+ - } + field_constructor!(@METHODS $($rest)*); + }; + (@METHODS) => {}; - assert_eq!(field_set, EXPECTED_FIELD_SET, "Not all fields found in {}", stringify!($class_name)); + ( + @CHECKS + $field_ident:ident, + $field_set_ident:ident, - $( - unsafe { - $inner_mod::init_offsets(); - } - )? + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @FIELD $field_name:ident: $matcher:pat $(if $guard:expr)?, $($rest:tt)* + ) => { + paste::paste! { + if $field_ident.name == get_sym!($($specified_sym_name)? $field_name) && matches!(&$field_ident.descriptor, $matcher $(if $guard)?) { + $field_set_ident |= 1 << instance_field_count!($($rest)*); + unsafe { []($field_ident.index()); } + continue; } } - $( - pub mod $inner_mod { - use super::*; + field_constructor!(@CHECKS $field_ident, $field_set_ident, $($rest)*); + }; + ( + @CHECKS + $field_ident:ident, + $field_set_ident:ident, - field_module!( - $($inner_mod_tt)* - ); + $(#[$meta:meta])* + $([sym: $specified_sym_name:ident])? + @INJECTED $field_name:ident: $_descriptor:expr => $field_ty:ty, $($rest:tt)* + ) => { + // Injected fields are not checked, in the field set, we only need to set their ids + paste::paste! { + if $field_ident.name == get_sym!($($specified_sym_name)? $field_name) { + unsafe { []($field_ident.index()); } + continue; } - )? - } + } + + field_constructor!(@CHECKS $field_ident, $field_set_ident, $($rest)*); + }; + ( + @CHECKS + $field_ident:ident, + $field_set_ident:ident, + ) => {}; +} + +// TODO: Document +macro_rules! field_module { + ( + @CLASS $class_name:ident; + $(@SUBCLASS $sub_class_name:ident;)? + + @FIELDSTART + $($field_tt:tt)* + ) => { + field_constructor!($class_name $($sub_class_name)? @FIELDSTART $($field_tt)*); + }; } pub mod java_lang_ref_Reference { @@ -89,6 +217,7 @@ pub mod java_lang_ref_Reference { field_module! { @CLASS java_lang_ref_Reference; + @FIELDSTART /// `java.lang.ref.Reference#referent` field offset /// /// Expected type: `Reference` @@ -102,6 +231,7 @@ pub mod java_lang_Class { field_module! { @CLASS java_lang_Class; + @FIELDSTART /// `java.lang.Class#name` field offset /// /// Expected type: `Reference` to `java.lang.String` @@ -117,12 +247,56 @@ pub mod java_lang_Class { } } +pub mod java_lang_ClassLoader { + use crate::classpath::loader::ClassLoader; + use crate::objects::instance::Instance; + use crate::objects::reference::Reference; + use classfile::FieldType; + + pub fn injected_loader_ptr_for(obj: Reference) -> Option<*const ClassLoader> { + let ptr = obj + .get_field_value0(loader_ptr_field_offset()) + .expect_long(); + if ptr == 0 { + // Field not initialized yet. + return None; + } + + Some(ptr as *const ClassLoader) + } + + field_module! { + @CLASS java_lang_ClassLoader; + + @FIELDSTART + /// [`ClassLoader`] pointer field + /// + /// Expected type: `jlong` + /// [`ClassLoader`]: crate::classpath::loader::ClassLoader + @INJECTED loader_ptr: FieldType::Long => jni::sys::jlong, + + /// `java.lang.ClassLoader#name` field offset + /// + /// Expected type: `Reference` to `java.lang.String` + @FIELD name: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/String"), + /// `java.lang.ClassLoader#unnamedModule` field offset + /// + /// Expected type: `Reference` to `java.lang.Module` + @FIELD unnamedModule: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/Module"), + /// `java.lang.ClassLoader#nameAndId` field offset + /// + /// Expected type: `Reference` to `java.lang.String` + @FIELD nameAndId: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/String"), + } +} + pub mod java_lang_String { use classfile::FieldType; field_module! { @CLASS java_lang_String; + @FIELDSTART /// `java.lang.String#value` field offset /// /// Expected type: `jByteArray` @@ -135,11 +309,33 @@ pub mod java_lang_String { } pub mod java_lang_Module { + use crate::modules::Module; + use crate::objects::instance::Instance; + use crate::objects::reference::Reference; use classfile::FieldType; + pub fn injected_module_ptr_for(obj: Reference) -> Option<*const Module> { + let ptr = obj + .get_field_value0(module_ptr_field_offset()) + .expect_long(); + if ptr == 0 { + // Field not initialized yet. + return None; + } + + Some(ptr as *const Module) + } + field_module! { @CLASS java_lang_Module; + @FIELDSTART + /// [`Module`] pointer field + /// + /// Expected type: `jlong` + /// [`Module`]: crate::modules::Module + @INJECTED module_ptr: FieldType::Long => jni::sys::jlong, + /// `java.lang.Module#name` field offset /// /// Expected type: `Reference` to `java.lang.String` @@ -156,7 +352,9 @@ pub mod java_lang_Thread { field_module! { @CLASS java_lang_Thread; + @SUBCLASS holder; + @FIELDSTART /// `java.lang.Thread#eetop` field offset /// /// Expected type: `jlong` @@ -165,10 +363,15 @@ pub mod java_lang_Thread { /// /// Expected type: `Reference` to `java.lang.Thread$FieldHolder` @FIELD holder: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/Thread$FieldHolder"), + } + + pub mod holder { + use super::*; - mod holder { + field_module! { @CLASS java_lang_Thread_FieldHolder; + @FIELDSTART /// `java.lang.Thread$FieldHolder#stackSize` field offset /// /// Expected field type: `jlong` @@ -195,6 +398,7 @@ pub mod java_lang_StackTraceElement { field_module! { @CLASS java_lang_StackTraceElement; + @FIELDSTART /// `java.lang.StackTraceElement#declaringClassObject` field offset /// /// Expected field type: `Reference` to `java.lang.Class` @@ -236,6 +440,7 @@ pub mod java_lang_Throwable { field_module! { @CLASS java_lang_Throwable; + @FIELDSTART /// `java.lang.Throwable#stackTrace` field offset /// /// Expected field type: `Reference` to `StackTraceElement[]` @@ -257,6 +462,7 @@ pub mod java_io_File { field_module! { @CLASS java_io_File; + @FIELDSTART /// `java.io.File#path` field offset /// /// Expected field type: `Reference` to `java.lang.String` @@ -271,6 +477,7 @@ pub mod java_io_FileDescriptor { field_module! { @CLASS java_io_FileDescriptor; + @FIELDSTART /// `java.io.FileDescriptor#fd` field offset /// /// Expected field type: `jint` @@ -285,6 +492,7 @@ pub mod java_io_FileDescriptor { field_module! { @CLASS java_io_FileDescriptor; + @FIELDSTART /// `java.io.FileDescriptor#fd` field offset /// /// Expected field type: `jint` @@ -306,6 +514,7 @@ pub mod java_io_FileInputStream { field_module! { @CLASS java_io_FileInputStream; + @FIELDSTART /// `java.io.FileInputStream#fd` field offset /// /// Expected field type: `Reference` to `java.io.FileDescriptor` @@ -319,6 +528,7 @@ pub mod java_io_FileOutputStream { field_module! { @CLASS java_io_FileOutputStream; + @FIELDSTART /// `java.io.FileOutputStream#fd` field offset /// /// Expected field type: `Reference` to `java.io.FileDescriptor` @@ -334,6 +544,7 @@ pub mod jdk_internal_misc_UnsafeConstants { field_module! { @CLASS jdk_internal_misc_UnsafeConstants; + @FIELDSTART /// `jdk.internal.misc.UnsafeConstants#ADDRESS_SIZE0` field offset /// /// Expected field type: `jint` diff --git a/runtime/src/initialization.rs b/runtime/src/initialization.rs index a1b8cf4..1d9ac99 100644 --- a/runtime/src/initialization.rs +++ b/runtime/src/initialization.rs @@ -1,5 +1,6 @@ -use crate::classpath::classloader::ClassLoader; +use crate::classpath::loader::ClassLoader; use crate::java_call; +use crate::modules::Module; use crate::native::jni::invocation_api::main_java_vm; use crate::objects::class_instance::ClassInstance; use crate::objects::reference::Reference; @@ -9,18 +10,20 @@ use crate::thread::{JavaThread, JavaThreadBuilder}; use classfile::accessflags::MethodAccessFlags; use common::int_types::s4; use instructions::Operand; +use jni::error::JniError; use jni::java_vm::JavaVm; -use jni::sys::JavaVMInitArgs; +use jni::sys::{JavaVM, JavaVMInitArgs, JNI_ERR, JNI_OK}; use symbols::sym; -pub fn create_java_vm(args: Option<&JavaVMInitArgs>) -> JavaVm { +pub fn create_java_vm(args: Option<&JavaVMInitArgs>) -> Result { let thread = JavaThreadBuilder::new().finish(); unsafe { JavaThread::set_current_thread(thread); } - initialize_thread(JavaThread::current()); - unsafe { main_java_vm() } + initialize_thread(JavaThread::current())?; + + Ok(unsafe { main_java_vm() }) } /// The entire initialization stage of the VM @@ -38,8 +41,8 @@ pub fn create_java_vm(args: Option<&JavaVMInitArgs>) -> JavaVm { /// 4. Create the initial `java.lang.Thread` for the current thread. /// /// [`create_java_base()`]: crate::modules::ModuleLockGuard::create_java_base() -fn initialize_thread(thread: &JavaThread) { - crate::modules::with_module_lock(|guard| guard.create_java_base()); +fn initialize_thread(thread: &JavaThread) -> Result<(), JniError> { + crate::modules::with_module_lock(|guard| Module::create_java_base(guard)); // Load some important classes load_global_classes(); @@ -57,17 +60,19 @@ fn initialize_thread(thread: &JavaThread) { // https://github.com/openjdk/jdk/blob/04591595374e84cfbfe38d92bff4409105b28009/src/hotspot/share/runtime/threads.cpp#L408 - init_phase_1(thread); + init_phase_1(thread)?; // TODO: ... - init_phase_2(thread); + init_phase_2(thread)?; // TODO: ... - init_phase_3(thread); + init_phase_3(thread)?; // TODO: https://github.com/openjdk/jdk/blob/04591595374e84cfbfe38d92bff4409105b28009/src/java.base/share/native/libjli/java.c#L1805 + + Ok(()) } fn load_global_classes() { @@ -140,6 +145,11 @@ fn init_field_offsets() { crate::globals::fields::java_lang_Class::init_offsets(); } + // java.lang.ClassLoader + unsafe { + crate::globals::fields::java_lang_ClassLoader::init_offsets(); + } + // java.lang.String unsafe { crate::globals::fields::java_lang_String::init_offsets(); @@ -219,41 +229,58 @@ fn create_thread_object(thread: &JavaThread) { /// * Signal handlers /// * OS-specific system settings /// * Thread group of the main thread -fn init_phase_1(thread: &JavaThread) { +fn init_phase_1(thread: &JavaThread) -> Result<(), JniError> { let system_class = crate::globals::classes::java_lang_System(); let init_phase_1 = system_class .resolve_method_step_two(sym!(initPhase1_name), sym!(void_method_signature)) .unwrap(); java_call!(thread, init_phase_1); + + if thread.has_pending_exception() { + return Err(JniError::ExceptionThrown); + } + + Ok(()) } /// Call `java.lang.System#initPhase2` /// /// This is responsible for initializing the module system. Prior to this point, the only module /// available to us is `java.base`. -fn init_phase_2(thread: &JavaThread) { +fn init_phase_2(thread: &JavaThread) -> Result<(), JniError> { let system_class = crate::globals::classes::java_lang_System(); // TODO: Actually set these arguments accordingly let display_vm_output_to_stderr = false; let print_stacktrace_on_exception = true; - // TODO: Need some way to check failure let init_phase_2 = system_class .resolve_method_step_two(sym!(initPhase2_name), sym!(bool_bool_int_signature)) .unwrap(); - java_call!( + let ret = java_call!( thread, init_phase_2, display_vm_output_to_stderr as s4, print_stacktrace_on_exception as s4 - ); + ) + .unwrap() + .expect_int(); + + if ret != JNI_OK { + return Err(JniError::Unknown); + } + + if thread.has_pending_exception() { + return Err(JniError::ExceptionThrown); + } unsafe { crate::globals::modules::set_module_system_initialized(); } + + Ok(()) } /// Call `java.lang.System#initPhase3` @@ -263,7 +290,7 @@ fn init_phase_2(thread: &JavaThread) { /// * Initialization of and setting the security manager /// * Setting the system class loader /// * Setting the thread context class loader -fn init_phase_3(thread: &JavaThread) { +fn init_phase_3(thread: &JavaThread) -> Result<(), JniError> { let system_class = crate::globals::classes::java_lang_System(); let init_phase_3 = system_class @@ -271,4 +298,10 @@ fn init_phase_3(thread: &JavaThread) { .unwrap(); java_call!(thread, init_phase_3); + + if thread.has_pending_exception() { + return Err(JniError::ExceptionThrown); + } + + Ok(()) } diff --git a/runtime/src/interpreter.rs b/runtime/src/interpreter.rs index 8c4f17d..be2b7f2 100644 --- a/runtime/src/interpreter.rs +++ b/runtime/src/interpreter.rs @@ -1,6 +1,5 @@ #![allow(unused_imports)] // Intellij-Rust doesn't like this file much, the imports used in macros are not recognized -use crate::classpath::classloader::ClassLoader; use crate::method_invoker::MethodInvoker; use crate::objects::array::ArrayInstance; use crate::objects::class::{Class, ClassInitializationState}; @@ -18,6 +17,7 @@ use std::cmp::Ordering; use std::sync::atomic::Ordering as MemOrdering; use std::sync::Arc; +use crate::thread::exceptions::handle_exception; use classfile::ConstantPoolValueInfo; use common::int_types::{s2, s4, s8, u2}; use common::traits::PtrType; @@ -704,6 +704,9 @@ impl Interpreter { // ========= Extended ========= // TODO: wide, multianewarray, jsr_w CATEGORY: extended + OpCode::multianewarray => { + Self::multianewarray(frame); + }, OpCode::ifnull => { let reference = frame.stack_mut().pop_reference(); @@ -1006,4 +1009,21 @@ impl Interpreter { frame.thread().pc.store(opcode_address + offset, MemOrdering::Relaxed); } + + fn multianewarray(frame: &mut Frame) { + let index = frame.read_byte2(); + let dimensions = frame.read_byte(); + + assert!(dimensions >= 1); + + let constant_pool = frame.constant_pool(); + let class = constant_pool.get::(index); + + class.initialize(frame.thread()); + + let counts = frame.stack_mut().popn(dimensions as usize); + + let array_ref = handle_exception!(frame.thread(), ArrayInstance::new_multidimensional(counts.into_iter().map(|op| op.expect_int()), class)); + frame.stack_mut().push_reference(Reference::array(array_ref)); + } } diff --git a/runtime/src/modules/entry.rs b/runtime/src/modules/entry.rs index 3f10d8f..3c85684 100644 --- a/runtime/src/modules/entry.rs +++ b/runtime/src/modules/entry.rs @@ -1,27 +1,55 @@ -use super::package::Package; -use crate::classpath::classloader::ClassLoader; +use super::package::{Package, PackageExportType}; use crate::classpath::jimage; +use crate::classpath::loader::{ClassLoader, ClassLoaderSet}; use crate::objects::instance::Instance; use crate::objects::reference::Reference; use crate::string_interner::StringInterner; use crate::thread::exceptions::{throw, Throws}; use std::cell::SyncUnsafeCell; +use std::collections::HashSet; use std::fmt::{Debug, Formatter}; -use std::sync::Arc; +use std::hash::Hash; +use common::traits::PtrType; +use instructions::Operand; +use jni::sys::jlong; use symbols::{sym, Symbol}; -// NOTE: The fields are `UnsafeCell`s due to the bootstrapping process. -// Mutation does not occur outside of `java.base`. +struct ModuleFlags { + can_read_all_unnamed: bool, +} /// Representation of a `java.lang.Module` object pub struct Module { - pub(super) obj: SyncUnsafeCell, - pub(super) open: bool, - pub(super) name: Option, - pub(super) version: SyncUnsafeCell>, - pub(super) location: SyncUnsafeCell>, + name: Option, + + // These fields are only ever mutated while the module lock is held + flags: SyncUnsafeCell, + reads: SyncUnsafeCell>, + loader: SyncUnsafeCell>, // Set in `ModuleSet::add()` + + open: bool, + + // NOTE: These fields are `UnsafeCell`s due to the bootstrapping process. + // Mutation does not occur outside of `java.base`. + obj: SyncUnsafeCell, + version: SyncUnsafeCell>, + location: SyncUnsafeCell>, +} + +impl PartialEq for Module { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl Eq for Module {} + +impl Hash for Module { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } } impl Debug for Module { @@ -43,6 +71,68 @@ impl Debug for Module { } } +// Bootstrapping methods +impl Module { + /// Create the partial `java.base` entry for bootstrapping purposes + /// + /// The [`Module`] produced is **not valid**. + pub(crate) fn create_java_base(_guard: &super::ModuleLockGuard) { + // This doesn't use the constructors, since they do validation. + let java_base = Module { + flags: SyncUnsafeCell::new(ModuleFlags { + can_read_all_unnamed: false, + }), + reads: SyncUnsafeCell::new(HashSet::new()), + loader: SyncUnsafeCell::new(None), + + name: Some(sym!(java_base)), + open: false, + obj: SyncUnsafeCell::new(Reference::null()), + version: SyncUnsafeCell::new(None), + location: SyncUnsafeCell::new(None), + }; + + ClassLoader::bootstrap().set_java_base(java_base); + } + + /// Make `java.base` into a real module + /// + /// # Safety + /// + /// This must be called on the `java.base` [`Module`] + pub(super) unsafe fn fixup_java_base( + &'static self, + _guard: &super::ModuleLockGuard, + obj: Reference, + version: Option, + location: Option, + ) { + assert!( + !self.has_obj(), + "java.base module can only be initialized once" + ); + + assert!(obj.is_class(), "invalid associated object for module"); + + unsafe { + *self.loader.get() = Some(ClassLoader::bootstrap()); + + *self.obj.get() = obj.clone(); + *self.version.get() = version; + *self.location.get() = location; + } + + // Store the pointer in the module, to make future lookups cheaper + obj.extract_class().get_mut().put_field_value0( + crate::globals::fields::java_lang_Module::module_ptr_field_offset(), + Operand::Long(self as *const Module as jlong), + ); + + // All classes we've loaded up to this point need to be added to `java.base` + ClassLoader::fixup_modules(obj); + } +} + impl Module { /// Create an unnamed `Module` /// @@ -68,6 +158,12 @@ impl Module { } Throws::Ok(Self { + flags: SyncUnsafeCell::new(ModuleFlags { + can_read_all_unnamed: true, + }), + reads: SyncUnsafeCell::new(HashSet::new()), + loader: SyncUnsafeCell::new(None), + obj: SyncUnsafeCell::new(obj), open: true, name: None, @@ -92,12 +188,12 @@ impl Module { throw!(@DEFER IllegalArgumentException, "Module name cannot be null"); } - let module_name = StringInterner::symbol_from_java_string(name_obj.extract_class()); + let module_name = StringInterner::rust_string_from_java_string(name_obj.extract_class()); let loader = obj .get_field_value0(crate::globals::fields::java_lang_Module::loader_field_offset()) .expect_reference(); - if module_name == sym!(java_base) { + if &module_name == "java.base" { init_java_base(obj, is_open, version, location, package_names, loader); return Throws::Ok(()); } @@ -124,16 +220,18 @@ impl Module { package_symbols.push(Symbol::intern_owned(package)); } - let loader = ClassLoader::from_obj(loader).expect("module must have a valid loader"); + let loader = ClassLoaderSet::find_or_add(loader); if let Some(disallowed_package) = disallowed_package { throw!(@DEFER IllegalArgumentException, "Class loader (instance of): {} tried to define prohibited package name: {}", - loader.name().as_str(), + loader.name_and_id().as_str(), disallowed_package.replace('/', ".") ); } + let module_name_sym = Symbol::intern_owned(module_name); + let mut module_already_defined = false; let mut duplicate_package: Option<&Package> = None; super::with_module_lock(|guard| { @@ -143,7 +241,7 @@ impl Module { duplicate_package = Some(duplicate); // Also check for a duplicate module. That error takes precedence over the duplicate package. - if loader.lookup_module(guard, module_name).is_some() { + if loader.lookup_module(guard, module_name_sym).is_some() { module_already_defined = true; } @@ -152,9 +250,15 @@ impl Module { } let module_entry = Self { + flags: SyncUnsafeCell::new(ModuleFlags { + can_read_all_unnamed: false, + }), + reads: SyncUnsafeCell::new(HashSet::new()), + loader: SyncUnsafeCell::new(None), + obj: SyncUnsafeCell::new(obj), open: is_open, - name: Some(module_name), + name: Some(module_name_sym), version: SyncUnsafeCell::new(version), location: SyncUnsafeCell::new(location), }; @@ -162,13 +266,13 @@ impl Module { let module = loader.insert_module(guard, module_entry); for package in package_symbols { - let package = Package::new(package, Arc::clone(&module)); + let package = Package::new(package, module); loader.insert_package_if_absent(guard, package); } }); if module_already_defined { - throw!(@DEFER IllegalStateException, "Module {} is already defined", module_name.as_str()); + throw!(@DEFER IllegalStateException, "Module {} is already defined", module_name_sym.as_str()); } if let Some(duplicate_package) = duplicate_package { @@ -177,7 +281,7 @@ impl Module { throw!(@DEFER IllegalStateException, "Package {} for module {} is already in another module, {}, defined to the class loader", duplicate_package.name().as_str(), - module_name.as_str(), + module_name_sym.as_str(), name.as_str(), ); }, @@ -185,7 +289,7 @@ impl Module { throw!(@DEFER IllegalStateException, "Package {} for module {} is already in the unnamed module defined to the class loader", duplicate_package.name().as_str(), - module_name.as_str() + module_name_sym.as_str() ); }, } @@ -245,11 +349,11 @@ fn init_java_base( return; } - let package = Package::new(Symbol::intern_owned(package), Arc::clone(&java_base)); + let package = Package::new(Symbol::intern_owned(package), java_base); ClassLoader::bootstrap().insert_package_if_absent(guard, package); } - guard.fixup_java_base(&java_base, obj, version, location) + unsafe { java_base.fixup_java_base(guard, obj, version, location) } }); if let Some(bad_package_name) = bad_package_name { @@ -272,6 +376,19 @@ impl Module { self.name } + pub fn is_open(&self) -> bool { + self.open + } + + // Called in `ModuleSet::add` + pub(super) fn set_classloader(&mut self, loader: &'static ClassLoader) { + *self.loader.get_mut() = Some(loader); + } + + pub fn classloader(&self) -> &'static ClassLoader { + unsafe { *self.loader.get() }.expect("loader should always be available") + } + pub fn version(&self) -> Option { unsafe { *self.version.get() } } @@ -295,4 +412,46 @@ impl Module { let obj_ref = unsafe { &*obj_ptr }; !obj_ref.is_null() } + + pub fn add_reads(&self, other: Option<&'static Self>) -> Throws<()> { + if self.name.is_none() { + // Nothing to do + return Throws::Ok(()); + } + + super::with_module_lock(|_guard| { + let flags_ptr = self.flags.get(); + + let Some(other) = other else { + unsafe { (&mut *flags_ptr).can_read_all_unnamed = true }; + return; + }; + + let reads = unsafe { &mut *self.reads.get() }; + reads.insert(other); + }); + + Throws::Ok(()) + } + + pub fn add_exports(&self, other: Option<&'static Self>, package_name: String) { + if self.name().is_none() || self.is_open() { + // Nothing to do if `from` is unnamed or open. All packages are exported by default. + return; + } + + super::with_module_lock(|guard| { + let Some(package) = self + .classloader() + .lookup_package(guard, Symbol::intern_owned(package_name)) + else { + return; + }; + + match other { + Some(other) => package.add_qualified_export(guard, other), + None => package.set_export_type(guard, PackageExportType::Unqualified), + } + }) + } } diff --git a/runtime/src/modules/mod.rs b/runtime/src/modules/mod.rs index 95c12e8..7e4eab1 100644 --- a/runtime/src/modules/mod.rs +++ b/runtime/src/modules/mod.rs @@ -1,16 +1,13 @@ -use crate::classpath::classloader::ClassLoader; -use crate::objects::reference::Reference; - -use std::cell::SyncUnsafeCell; use std::sync::{Mutex, MutexGuard}; -use symbols::{sym, Symbol}; - mod entry; pub use entry::Module; mod package; -pub use package::Package; +pub use package::{Package, PackageExportType}; + +mod set; +pub use set::ModuleSet; static MODULE_LOCK: Mutex<()> = Mutex::new(()); @@ -27,45 +24,3 @@ where let _guard = MODULE_LOCK.lock().unwrap(); f(&ModuleLockGuard(_guard)); } - -impl ModuleLockGuard { - /// 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(&self) { - // Don't use the constructors, since they do validation. - let java_base_module = Module { - name: Some(sym!(java_base)), - open: false, - obj: SyncUnsafeCell::new(Reference::null()), - version: SyncUnsafeCell::new(None), - location: SyncUnsafeCell::new(None), - }; - - ClassLoader::bootstrap().set_java_base(java_base_module); - } - - pub fn fixup_java_base( - &self, - java_base: &Module, - obj: Reference, - version: Option, - location: Option, - ) { - assert!( - !java_base.has_obj(), - "java.base module can only be initialized once" - ); - - assert!(obj.is_class(), "invalid associated object for module"); - unsafe { - *java_base.obj.get() = obj.clone(); - *java_base.version.get() = version; - *java_base.location.get() = location; - } - - // All classes we've loaded up to this point need to be added to `java.base` - ClassLoader::fixup_modules(obj); - } -} diff --git a/runtime/src/modules/package.rs b/runtime/src/modules/package.rs index 1e6c491..b4cd436 100644 --- a/runtime/src/modules/package.rs +++ b/runtime/src/modules/package.rs @@ -1,7 +1,9 @@ use super::entry::Module; +use crate::modules::ModuleLockGuard; +use std::cell::SyncUnsafeCell; +use std::collections::HashSet; use std::fmt::Debug; -use std::sync::Arc; use symbols::Symbol; @@ -20,9 +22,11 @@ pub enum PackageExportType { /// A representation of a package in Java pub struct Package { name: Symbol, - module: Arc, + module: &'static Module, - export_type: PackageExportType, + // These fields are only ever mutated while the module lock is held + qualified_exports: SyncUnsafeCell>, + export_type: SyncUnsafeCell, } impl Debug for Package { @@ -36,11 +40,12 @@ impl Debug for Package { } impl Package { - pub fn new(name: Symbol, module: Arc) -> Package { + pub fn new(name: Symbol, module: &'static Module) -> Package { Self { name, module, - export_type: PackageExportType::None, + qualified_exports: SyncUnsafeCell::new(HashSet::new()), + export_type: SyncUnsafeCell::new(PackageExportType::None), } } @@ -48,8 +53,16 @@ impl Package { self.name } - pub fn module(&self) -> Arc { - Arc::clone(&self.module) + pub fn module(&self) -> &'static Module { + self.module + } + + pub fn set_export_type(&self, _guard: &ModuleLockGuard, export_type: PackageExportType) { + unsafe { *self.export_type.get() = export_type } + } + + pub fn add_qualified_export(&self, _guard: &ModuleLockGuard, module: &'static Module) { + unsafe { &mut *self.qualified_exports.get() }.insert(module); } } diff --git a/runtime/src/modules/set.rs b/runtime/src/modules/set.rs new file mode 100644 index 0000000..4657794 --- /dev/null +++ b/runtime/src/modules/set.rs @@ -0,0 +1,65 @@ +use super::{Module, ModuleLockGuard}; +use crate::classpath::loader::ClassLoader; +use crate::objects::instance::Instance; + +use std::cell::SyncUnsafeCell; +use std::collections::LinkedList; + +use common::traits::PtrType; +use instructions::Operand; +use jni::sys::jlong; +use symbols::Symbol; + +/// A list of [`Module`]s, synchronized with the global [module lock] +/// +/// [module lock]: crate::modules::with_module_lock +pub struct ModuleSet { + // A list of all currently loaded modules + // + // This is a `LinkedList`, as reads will do a lifetime-extension, and are not guarded. We cannot + // risk a realloc invalidating a reference. + list: SyncUnsafeCell>, +} + +impl ModuleSet { + pub fn new() -> Self { + Self { + list: SyncUnsafeCell::new(LinkedList::new()), + } + } + + pub fn add(&self, _guard: &ModuleLockGuard, mut module: Module) -> &'static Module { + let obj = module.obj(); + + // Set `Module::loader` field. This gives us access to the packages from `ClassLoader` + let obj_class_loader = obj.extract_target_class().loader().obj(); + if obj_class_loader.is_null() { + module.set_classloader(ClassLoader::bootstrap()) + } else { + let classloader_ptr = + crate::globals::fields::java_lang_ClassLoader::injected_loader_ptr_for( + obj_class_loader, + ) + .expect("classloader should be initialized"); + module.set_classloader(unsafe { &*classloader_ptr }) + } + + let list = unsafe { &mut *self.list.get() }; + list.push_back(module); + + let ret = list.back().unwrap(); + + // Store the pointer in the module, to make future lookups cheaper + obj.extract_class().get_mut().put_field_value0( + crate::globals::fields::java_lang_Module::module_ptr_field_offset(), + Operand::Long(ret as *const Module as jlong), + ); + + ret + } + + pub fn find(&self, _guard: &ModuleLockGuard, name: Symbol) -> Option<&'static Module> { + let list = unsafe { &*self.list.get() }; + list.iter().find(|m| m.name() == Some(name)) + } +} diff --git a/runtime/src/native/java/io/FileDescriptor.rs b/runtime/src/native/java/io/FileDescriptor.rs index 23c01ce..44e8de8 100644 --- a/runtime/src/native/java/io/FileDescriptor.rs +++ b/runtime/src/native/java/io/FileDescriptor.rs @@ -18,12 +18,12 @@ pub fn get_fd(this: &Reference) -> jint { } // throws SyncFailedException -pub fn sync0(_: NonNull, _this: Reference) { +pub fn sync0(_: JniEnv, _this: Reference) { unimplemented!("java.io.FileDescriptor#sync0"); } // TODO: Move logic to globals -pub fn initIDs(_: NonNull, class: &'static Class) { +pub fn initIDs(_: JniEnv, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) @@ -40,23 +40,23 @@ pub fn initIDs(_: NonNull, class: &'static Class) { } #[cfg(windows)] -pub fn getHandle(_: NonNull, _class: &'static Class, _d: jint) -> jlong { +pub fn getHandle(_: JniEnv, _class: &'static Class, _d: jint) -> jlong { unimplemented!("java.io.FileDescriptor#getHandle"); } // Only windows uses the `handle` field. #[cfg(unix)] -pub fn getHandle(_: NonNull, _class: &'static Class, _d: jint) -> jlong { +pub fn getHandle(_: JniEnv, _class: &'static Class, _d: jint) -> jlong { -1 } #[cfg(windows)] -pub fn getAppend(_: NonNull, _class: &'static Class, _fd: jint) -> jboolean { +pub fn getAppend(_: JniEnv, _class: &'static Class, _fd: jint) -> jboolean { unimplemented!("java.io.FileDescriptor#getAppend"); } #[cfg(unix)] -pub fn getAppend(_: NonNull, _class: &'static Class, fd_: jint) -> jboolean { +pub fn getAppend(_: JniEnv, _class: &'static Class, fd_: jint) -> jboolean { use libc::{F_GETFL, O_APPEND}; let flags = unsafe { libc::fcntl(fd_, F_GETFL) }; @@ -64,6 +64,6 @@ pub fn getAppend(_: NonNull, _class: &'static Class, fd_: jint) -> jbool } // throws IOException -pub fn close0(_: NonNull, _this: Reference) { +pub fn close0(_: JniEnv, _this: Reference) { unimplemented!("java.io.FileDescriptor#close0"); } diff --git a/runtime/src/native/java/io/FileInputStream.rs b/runtime/src/native/java/io/FileInputStream.rs index 978f5eb..0c7e92c 100644 --- a/runtime/src/native/java/io/FileInputStream.rs +++ b/runtime/src/native/java/io/FileInputStream.rs @@ -12,18 +12,18 @@ use ::jni::sys::{jboolean, jint, jlong}; include_generated!("native/java/io/def/FileInputStream.definitions.rs"); // throws FileNotFoundException -pub fn open0(_: NonNull, _this: Reference, _name: Reference /* java.lang.String */) { +pub fn open0(_: JniEnv, _this: Reference, _name: Reference /* java.lang.String */) { unimplemented!("java.io.FileInputStream#open0"); } // throws IOException -pub fn read0(_: NonNull, _this: Reference) -> jint { +pub fn read0(_: JniEnv, _this: Reference) -> jint { unimplemented!("java.io.FileInputStream#read0"); } // throws IOException pub fn readBytes( - _: NonNull, + _: JniEnv, _this: Reference, _b: Reference, // byte[] _off: jint, @@ -33,34 +33,34 @@ pub fn readBytes( } // throws IOException -pub fn length0(_: NonNull, _this: Reference) -> jlong { +pub fn length0(_: JniEnv, _this: Reference) -> jlong { unimplemented!("java.io.FileInputStream#length0"); } // throws IOException -pub fn position0(_: NonNull, _this: Reference) -> jlong { +pub fn position0(_: JniEnv, _this: Reference) -> jlong { unimplemented!("java.io.FileInputStream#position0"); } // throws IOException -pub fn skip0(_: NonNull, _this: Reference, _n: jlong) -> jlong { +pub fn skip0(_: JniEnv, _this: Reference, _n: jlong) -> jlong { unimplemented!("java.io.FileInputStream#skip0"); } // throws IOException -pub fn available0(_: NonNull, _this: Reference) -> jint { +pub fn available0(_: JniEnv, _this: Reference) -> jint { unimplemented!("java.io.FileInputStream#available0"); } pub fn isRegularFile0( - _: NonNull, + _: JniEnv, _this: Reference, _fd: Reference, // java.io.FileDescriptor ) -> jboolean { unimplemented!("java.io.FileInputStream#isRegularFile0"); } -pub fn initIDs(_: NonNull, class: &'static Class) { +pub fn initIDs(_: JniEnv, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) diff --git a/runtime/src/native/java/io/FileOutputStream.rs b/runtime/src/native/java/io/FileOutputStream.rs index 4cc1994..7b33ccf 100644 --- a/runtime/src/native/java/io/FileOutputStream.rs +++ b/runtime/src/native/java/io/FileOutputStream.rs @@ -26,19 +26,19 @@ fn get_fd(this: &Reference) -> jint { } // throws FileNotFoundException -pub fn open0(_: NonNull, _this: Reference, _name: Reference /* java.lang.String */) { +pub fn open0(_: JniEnv, _this: Reference, _name: Reference /* java.lang.String */) { unimplemented!("java.io.FileOutputStream#open0"); } // throws IOException -pub fn write(_: NonNull, _this: Reference, _b: jint, _append: jboolean) { +pub fn write(_: JniEnv, _this: Reference, _b: jint, _append: jboolean) { unimplemented!("java.io.FileOutputStream#write"); } // throws IOException #[allow(trivial_numeric_casts)] pub fn writeBytes( - env: NonNull, + env: JniEnv, this: Reference, b: Reference, // byte[] off: jint, @@ -46,14 +46,14 @@ pub fn writeBytes( _append: jboolean, ) { if b.is_null() { - let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + let _thread = unsafe { JavaThread::for_env(env.raw()) }; panic!("NullPointerException"); // TODO } let array_instance = b.extract_array(); let array_content = array_instance.get().get_content().expect_byte(); if off < 0 || len < 0 || (off + len) as usize > array_content.len() { - let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + let _thread = unsafe { JavaThread::for_env(env.raw()) }; panic!("IndexOutOfBoundsException"); // TODO } @@ -73,7 +73,7 @@ pub fn writeBytes( while len > 0 { let current_fd = get_fd(&this); if current_fd == -1 { - let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + let _thread = unsafe { JavaThread::for_env(env.raw()) }; panic!("IOException, stream closed"); // TODO } @@ -82,7 +82,7 @@ pub fn writeBytes( ManuallyDrop::new(unsafe { std::fs::File::from_raw_fd(current_fd as RawFd) }); let Ok(n) = file.write(&window[offset..]) else { - let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + let _thread = unsafe { JavaThread::for_env(env.raw()) }; panic!("IOException"); // TODO }; @@ -92,7 +92,7 @@ pub fn writeBytes( } } -pub fn initIDs(_: NonNull, class: &'static Class) { +pub fn initIDs(_: JniEnv, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) diff --git a/runtime/src/native/java/io/UnixFileSystem.rs b/runtime/src/native/java/io/UnixFileSystem.rs index ffbdc61..a330b02 100644 --- a/runtime/src/native/java/io/UnixFileSystem.rs +++ b/runtime/src/native/java/io/UnixFileSystem.rs @@ -27,7 +27,7 @@ fn get_file_path(file: Reference) -> String { } pub fn canonicalize0( - _: NonNull, + _: JniEnv, _this: Reference, path: Reference, // java.lang.String ) -> Reference /* java.lang.String */ { @@ -48,7 +48,7 @@ pub fn canonicalize0( #[cfg(unix)] pub fn getBooleanAttributes0( - _: NonNull, + _: JniEnv, _this: Reference, f: Reference, // java.io.File ) -> jint { @@ -78,7 +78,7 @@ pub fn getBooleanAttributes0( #[cfg(not(unix))] pub fn getBooleanAttributes0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File ) -> jint { @@ -86,7 +86,7 @@ pub fn getBooleanAttributes0( } pub fn checkAccess0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File _access: jint, @@ -95,7 +95,7 @@ pub fn checkAccess0( } pub fn getLastModifiedTime0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File ) -> jlong { @@ -103,7 +103,7 @@ pub fn getLastModifiedTime0( } pub fn getLength0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File ) -> jlong { @@ -111,7 +111,7 @@ pub fn getLength0( } pub fn setPermission0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File _access: jint, @@ -122,7 +122,7 @@ pub fn setPermission0( } pub fn createFileExclusively0( - _: NonNull, + _: JniEnv, _this: Reference, _path: Reference, // java.lang.String ) -> jboolean { @@ -130,7 +130,7 @@ pub fn createFileExclusively0( } pub fn delete0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File ) -> jboolean { @@ -138,7 +138,7 @@ pub fn delete0( } pub fn list0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File ) -> Reference /* java.lang.String[] */ { @@ -146,7 +146,7 @@ pub fn list0( } pub fn createDirectory0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File ) -> jboolean { @@ -154,7 +154,7 @@ pub fn createDirectory0( } pub fn rename0( - _: NonNull, + _: JniEnv, _this: Reference, _f1: Reference, // java.io.File _f2: Reference, // java.io.File @@ -163,7 +163,7 @@ pub fn rename0( } pub fn setLastModifiedTime0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File _time: jlong, @@ -172,7 +172,7 @@ pub fn setLastModifiedTime0( } pub fn setReadOnly0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File ) -> jboolean { @@ -180,7 +180,7 @@ pub fn setReadOnly0( } pub fn getSpace0( - _: NonNull, + _: JniEnv, _this: Reference, _f: Reference, // java.io.File _t: jint, @@ -189,14 +189,14 @@ pub fn getSpace0( } pub fn getNameMax0( - _: NonNull, + _: JniEnv, _this: Reference, _path: Reference, // java.lang.String ) -> jlong { unimplemented!("java.io.UnixFileSystem#getNameMax0"); } -pub fn initIDs(_: NonNull, class: &'static Class) { +pub fn initIDs(_: JniEnv, class: &'static Class) { static ONCE: AtomicBool = AtomicBool::new(false); if ONCE .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) diff --git a/runtime/src/native/java/lang/Class.rs b/runtime/src/native/java/lang/Class.rs index 82b8364..797ea91 100644 --- a/runtime/src/native/java/lang/Class.rs +++ b/runtime/src/native/java/lang/Class.rs @@ -21,7 +21,7 @@ include_generated!("native/java/lang/def/Class.definitions.rs"); // throws ClassNotFoundException pub fn forName0( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _name: Reference, // java.lang.String _initialize: jboolean, @@ -32,19 +32,19 @@ pub fn forName0( } pub fn isInstance( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class _obj: Reference, // java.lang.Object ) -> jboolean { unimplemented!("Class#isInstance"); } pub fn isAssignableFrom( - env: NonNull, + env: JniEnv, this: Reference, // java.lang.Class cls: Reference, // java.lang.Class ) -> jboolean { if cls.is_null() { - let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + let _thread = unsafe { JavaThread::for_env(env.raw()) }; panic!("NullPointerException"); // TODO } @@ -52,21 +52,20 @@ pub fn isAssignableFrom( let sub = cls.extract_target_class(); let super_ = this.extract_target_class(); - let env = unsafe { &(*env.as_ptr()) }; env.is_assignable_from(sub.into_jni_safe(), super_.into_jni_safe()) } -pub fn isInterface(_env: NonNull, this: Reference /* java.lang.Class */) -> jboolean { +pub fn isInterface(_env: JniEnv, this: Reference /* java.lang.Class */) -> jboolean { this.extract_target_class().is_interface() } -pub fn isArray(_env: NonNull, this: Reference /* java.lang.Class */) -> jboolean { +pub fn isArray(_env: JniEnv, this: Reference /* java.lang.Class */) -> jboolean { this.extract_mirror().get().is_array() } -pub fn isPrimitive(_env: NonNull, this: Reference /* java.lang.Class */) -> jboolean { +pub fn isPrimitive(_env: JniEnv, this: Reference /* java.lang.Class */) -> jboolean { this.extract_mirror().get().is_primitive() } pub fn initClassName( - _env: NonNull, + _env: JniEnv, this: Reference, // java.lang.Class ) -> Reference { let this_mirror = this.extract_mirror(); @@ -83,12 +82,10 @@ pub fn initClassName( } pub fn getSuperclass( - env: NonNull, + env: JniEnv, this: Reference, // java.lang.Class ) -> Reference /* Class */ { - let env = unsafe { &*env.as_ptr() }; - let target_class = this.extract_target_class(); let Some(super_class_raw) = env.get_super_class(target_class.into_jni_safe()) else { return Reference::null(); @@ -98,56 +95,56 @@ pub fn getSuperclass( Reference::mirror(super_class.mirror()) } pub fn getInterfaces0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* Class[] */ { unimplemented!("Class#getInterfaces0"); } -pub fn getModifiers(_env: NonNull, _this: Reference /* java.lang.Class */) -> jint { +pub fn getModifiers(_env: JniEnv, _this: Reference /* java.lang.Class */) -> jint { unimplemented!("Class#getModifiers"); } -pub fn getSigners(_env: NonNull, _this: Reference /* java.lang.Class */) -> Reference /* Object[] */ +pub fn getSigners(_env: JniEnv, _this: Reference /* java.lang.Class */) -> Reference /* Object[] */ { unimplemented!("Class#getSigners"); } pub fn setSigners( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class _signers: Reference, // Object[] ) { unimplemented!("Class#setSigners"); } pub fn getEnclosingMethod0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* Object[] */ { unimplemented!("Class#getEnclosingMethod0"); } pub fn getDeclaringClass0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* Class */ { unimplemented!("Class#getDeclaringClass0"); } pub fn getSimpleBinaryName0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* String */ { unimplemented!("Class#getSimpleBinaryName0"); } pub fn getProtectionDomain0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* java.security.ProtectionDomain */ { unimplemented!("Class#getProtectionDomain0"); } pub fn getPrimitiveClass( - _env: NonNull, + _env: JniEnv, _class: &'static Class, name: Reference, // String ) -> Reference /* Class */ @@ -165,55 +162,55 @@ pub fn getPrimitiveClass( panic!("ClassNotFoundException") } pub fn getGenericSignature0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* String */ { unimplemented!("Class#getGenericSignature0"); } pub fn getRawAnnotations( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* byte[] */ { unimplemented!("Class#getRawAnnotations"); } pub fn getRawTypeAnnotations( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* byte[] */ { unimplemented!("Class#getRawTypeAnnotations"); } pub fn getConstantPool( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* ConstantPool */ { unimplemented!("Class#getConstantPool"); } pub fn getDeclaredFields0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class _public_only: jboolean, ) -> Reference /* Field[] */ { unimplemented!("Class#getDeclaredFields0"); } pub fn getDeclaredMethods0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class _public_only: jboolean, ) -> Reference /* Method[] */ { unimplemented!("Class#getDeclaredMethods0"); } pub fn getDeclaredConstructors0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class _public_only: jboolean, ) -> Reference /* Constructor[] */ { unimplemented!("Class#getDeclaredConstructors0"); } pub fn getDeclaredClasses0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* Class[] */ { @@ -221,20 +218,20 @@ pub fn getDeclaredClasses0( } pub fn getRecordComponents0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* RecordComponent[] */ { unimplemented!("Class#getRecordComponents0"); } -pub fn isRecord0(_env: NonNull, _this: Reference /* java.lang.Class */) -> jboolean { +pub fn isRecord0(_env: JniEnv, _this: Reference /* java.lang.Class */) -> jboolean { unimplemented!("Class#isRecord0"); } // TODO: https://github.com/openjdk/jdk/blob/19373b2ff0cd795afa262c17dcb3388fd6a5be59/src/hotspot/share/classfile/javaAssertions.cpp#L195 #[allow(clippy::unnecessary_wraps, clippy::no_effect_underscore_binding)] pub fn desiredAssertionStatus0( - _env: NonNull, + _env: JniEnv, _class: &'static Class, clazz: Reference, // java/lang/Class ) -> jboolean { @@ -245,7 +242,7 @@ pub fn desiredAssertionStatus0( } pub fn getNestHost0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* java/lang/Class */ { @@ -253,33 +250,33 @@ pub fn getNestHost0( } pub fn getNestMembers0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* Class[] */ { unimplemented!("Class#getNestMembers0"); } -pub fn isHidden(_env: NonNull, _this: Reference /* java.lang.Class */) -> jboolean { +pub fn isHidden(_env: JniEnv, _this: Reference /* java.lang.Class */) -> jboolean { unimplemented!("Class#isHidden"); } pub fn getPermittedSubclasses0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> Reference /* Class[] */ { unimplemented!("Class#getPermittedSubclasses0"); } pub fn getClassFileVersion0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> jint { unimplemented!("Class#getClassFileVersion0"); } pub fn getClassAccessFlagsRaw0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class ) -> jint { unimplemented!("Class#getClassAccessFlagsRaw0"); diff --git a/runtime/src/native/java/lang/ClassLoader.rs b/runtime/src/native/java/lang/ClassLoader.rs index 36d4d08..bd54eab 100644 --- a/runtime/src/native/java/lang/ClassLoader.rs +++ b/runtime/src/native/java/lang/ClassLoader.rs @@ -10,7 +10,7 @@ include_generated!("native/java/lang/def/ClassLoader.registerNatives.rs"); include_generated!("native/java/lang/def/ClassLoader.definitions.rs"); pub fn defineClass1( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _loader: Reference, // java.lang.ClassLoader _name: Reference, // java.lang.String @@ -25,7 +25,7 @@ pub fn defineClass1( } pub fn defineClass2( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _loader: Reference, // java.lang.ClassLoader _name: Reference, // java.lang.String @@ -40,7 +40,7 @@ pub fn defineClass2( } pub fn defineClass0( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _loader: Reference, // java.lang.ClassLoader _lookup: Reference, // java.lang.Class @@ -58,7 +58,7 @@ pub fn defineClass0( } pub fn findBootstrapClass( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _name: Reference, // java.lang.String ) -> Reference // java.lang.Class @@ -67,7 +67,7 @@ pub fn findBootstrapClass( } pub fn findLoadedClass0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Class _name: Reference, // java.lang.String ) -> Reference // java.lang.Class @@ -75,7 +75,7 @@ pub fn findLoadedClass0( unimplemented!("java.lang.ClassLoader#findLoadedClass0") } -pub fn retrieveDirectives(_env: NonNull, _class: &'static Class) -> Reference // AssertionStatusDirectives +pub fn retrieveDirectives(_env: JniEnv, _class: &'static Class) -> Reference // AssertionStatusDirectives { unimplemented!("java.lang.ClassLoader#retrieveDirectives") } diff --git a/runtime/src/native/java/lang/Double.rs b/runtime/src/native/java/lang/Double.rs index b05fae9..d3892a1 100644 --- a/runtime/src/native/java/lang/Double.rs +++ b/runtime/src/native/java/lang/Double.rs @@ -7,10 +7,10 @@ use common::int_types::{s8, u8}; include_generated!("native/java/lang/def/Double.definitions.rs"); -pub fn doubleToRawLongBits(_env: NonNull, _class: &'static Class, value: f64) -> s8 { +pub fn doubleToRawLongBits(_env: JniEnv, _class: &'static Class, value: f64) -> s8 { value.to_bits() as s8 } -pub fn longBitsToDouble(_env: NonNull, _class: &'static Class, bits: s8) -> f64 { +pub fn longBitsToDouble(_env: JniEnv, _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 1fa7bee..c05575a 100644 --- a/runtime/src/native/java/lang/Float.rs +++ b/runtime/src/native/java/lang/Float.rs @@ -1,16 +1,14 @@ use crate::objects::class::Class; -use std::ptr::NonNull; - use ::jni::env::JniEnv; use common::int_types::{s4, u4}; include_generated!("native/java/lang/def/Float.definitions.rs"); -pub fn floatToRawIntBits(_env: NonNull, _class: &'static Class, value: f32) -> s4 { +pub fn floatToRawIntBits(_env: JniEnv, _class: &'static Class, value: f32) -> s4 { value.to_bits() as s4 } -pub fn intBitsToFloat(_env: NonNull, _class: &'static Class, bits: s4) -> f32 { +pub fn intBitsToFloat(_env: JniEnv, _class: &'static Class, bits: s4) -> f32 { f32::from_bits(bits as u4) } diff --git a/runtime/src/native/java/lang/Module.rs b/runtime/src/native/java/lang/Module.rs index bc75962..1fb4a8b 100644 --- a/runtime/src/native/java/lang/Module.rs +++ b/runtime/src/native/java/lang/Module.rs @@ -5,15 +5,14 @@ use crate::string_interner::StringInterner; use crate::thread::exceptions::{handle_exception, throw}; use crate::thread::JavaThread; -use std::ptr::NonNull; - use ::jni::env::JniEnv; use common::traits::PtrType; +use symbols::Symbol; include_generated!("native/java/lang/def/Module.definitions.rs"); pub fn defineModule0( - env: NonNull, + env: JniEnv, _class: &'static Class, module: Reference, // java.lang.Module is_open: bool, @@ -21,20 +20,18 @@ pub fn defineModule0( location: Reference, // java.lang.String pns: Reference, // java.lang.Object[] ) { - let thread = unsafe { &*JavaThread::for_env(env.as_ptr()) }; + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; let mut version_sym = None; if !version.is_null() { - version_sym = Some(StringInterner::symbol_from_java_string( - version.extract_class(), - )); + let version_str = StringInterner::rust_string_from_java_string(version.extract_class()); + version_sym = Some(Symbol::intern_owned(version_str)); } let mut location_sym = None; if !location.is_null() { - location_sym = Some(StringInterner::symbol_from_java_string( - version.extract_class(), - )); + let location_str = StringInterner::rust_string_from_java_string(location.extract_class()); + location_sym = Some(Symbol::intern_owned(location_str)); } let mut package_names = Vec::new(); @@ -53,47 +50,127 @@ pub fn defineModule0( } } - let module_entry_result = Module::named( - module.clone(), - is_open, - version_sym, - location_sym, - package_names, + handle_exception!( + thread, + Module::named( + module.clone(), + is_open, + version_sym, + location_sym, + package_names, + ) ); - - handle_exception!(thread, module_entry_result); } pub fn addReads0( - _: NonNull, + env: JniEnv, _class: &'static Class, - _from: Reference, // java.lang.Module - _to: Reference, // java.lang.Module + from: Reference, // java.lang.Module + to: Reference, // java.lang.Module ) { - unimplemented!("java.lang.Module#addReads0"); + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; + + if from.is_null() { + throw!(thread, NullPointerException, "from_module is null"); + } + + if from == to { + // Nothing to do if the modules are the same + return; + } + + let Some(from_ptr) = crate::globals::fields::java_lang_Module::injected_module_ptr_for(from) + else { + throw!(thread, IllegalArgumentException, "from_module is not valid"); + }; + + let from_module = unsafe { &*from_ptr }; + if from_module.name().is_none() { + // Nothing to do if `from` is unnamed + return; + } + + let mut to_module = None; + if !to.is_null() { + let Some(to_ptr) = crate::globals::fields::java_lang_Module::injected_module_ptr_for(to) + else { + throw!(thread, IllegalArgumentException, "to_module is not valid"); + }; + + to_module = Some(unsafe { &*to_ptr }); + } + + from_module.add_reads(to_module); } pub fn addExports0( - _: NonNull, + env: JniEnv, _class: &'static Class, - _from: Reference, // java.lang.Module - _pn: Reference, // java.lang.String - _to: Reference, // java.lang.Module + from: Reference, // java.lang.Module + pn: Reference, // java.lang.String + to: Reference, // java.lang.Module ) { - unimplemented!("java.lang.Module#addExports0"); + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; + + if from.is_null() { + throw!(thread, NullPointerException, "from_module is null"); + } + + if to.is_null() { + throw!(thread, NullPointerException, "to_module is null"); + } + + if pn.is_null() { + throw!(thread, NullPointerException, "package is null"); + } + + let Some(from_ptr) = crate::globals::fields::java_lang_Module::injected_module_ptr_for(from) + else { + throw!(thread, IllegalArgumentException, "from_module is not valid"); + }; + + let Some(to_ptr) = crate::globals::fields::java_lang_Module::injected_module_ptr_for(to) else { + throw!(thread, IllegalArgumentException, "to_module is not valid"); + }; + + let package_name = StringInterner::rust_string_from_java_string(pn.extract_class()); + let package_name = Package::name_to_internal(package_name); + + let from_module = unsafe { &*from_ptr }; + let to_module = unsafe { &*to_ptr }; + from_module.add_exports(Some(to_module), package_name); } pub fn addExportsToAll0( - _: NonNull, + env: JniEnv, _class: &'static Class, - _from: Reference, // java.lang.Module - _pn: Reference, // java.lang.String + from: Reference, // java.lang.Module + pn: Reference, // java.lang.String ) { - unimplemented!("java.lang.Module#addExportsToAll0"); + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; + + if from.is_null() { + throw!(thread, NullPointerException, "from_module is null"); + } + + if pn.is_null() { + throw!(thread, NullPointerException, "package is null"); + } + + let Some(from_ptr) = crate::globals::fields::java_lang_Module::injected_module_ptr_for(from) + else { + throw!(thread, IllegalArgumentException, "from_module is not valid"); + }; + + let package_name = StringInterner::rust_string_from_java_string(pn.extract_class()); + let package_name = Package::name_to_internal(package_name); + + let from_module = unsafe { &*from_ptr }; + from_module.add_exports(None, package_name); } pub fn addExportsToAllUnnamed0( - _: NonNull, + _: JniEnv, _class: &'static Class, _from: Reference, // java.lang.Module _pn: Reference, // java.lang.String diff --git a/runtime/src/native/java/lang/Object.rs b/runtime/src/native/java/lang/Object.rs index 8ae8ece..ba1a66b 100644 --- a/runtime/src/native/java/lang/Object.rs +++ b/runtime/src/native/java/lang/Object.rs @@ -10,24 +10,24 @@ use common::traits::PtrType; include_generated!("native/java/lang/def/Object.definitions.rs"); -pub fn getClass(_: NonNull, this: Reference /* java.lang.Object */) -> Reference /* java.lang.Class */ +pub fn getClass(_: JniEnv, this: Reference /* java.lang.Object */) -> Reference /* java.lang.Class */ { Reference::mirror(this.extract_class_mirror()) } -pub fn hashCode(env: NonNull, this: Reference /* java.lang.Object */) -> jint { +pub fn hashCode(env: JniEnv, this: Reference /* java.lang.Object */) -> jint { // Hash already generated, nothing to do if let Some(hash) = this.hash() { return hash; } // We need to generate a hash, this will update the object for future calls - let thread = unsafe { &*JavaThread::for_env(env.as_ptr()) }; + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; this.generate_hash(thread) } // throws CloneNotSupportedException -pub fn clone(_: NonNull, this: Reference /* java.lang.Object */) -> Reference /* java.lang.Object */ +pub fn clone(_: JniEnv, this: Reference /* java.lang.Object */) -> Reference /* java.lang.Object */ { // An array is always cloneable if this.is_array() { @@ -48,16 +48,16 @@ pub fn clone(_: NonNull, this: Reference /* java.lang.Object */) -> Refe Reference::class(cloned) } -pub fn notify(_: NonNull, _this: Reference /* java.lang.Object */) { +pub fn notify(_: JniEnv, _this: Reference /* java.lang.Object */) { unimplemented!("Object#notify") } -pub fn notifyAll(_: NonNull, this: Reference /* java.lang.Object */) { +pub fn notifyAll(_: JniEnv, this: Reference /* java.lang.Object */) { this.notify_all(); } pub fn wait0( - _: NonNull, + _: JniEnv, _this: Reference, // java.lang.Object _timeout_millis: jlong, ) { diff --git a/runtime/src/native/java/lang/Runtime.rs b/runtime/src/native/java/lang/Runtime.rs index bf6f997..1d176d3 100644 --- a/runtime/src/native/java/lang/Runtime.rs +++ b/runtime/src/native/java/lang/Runtime.rs @@ -8,21 +8,21 @@ use common::int_types::{s4, s8}; include_generated!("native/java/lang/def/Runtime.definitions.rs"); pub fn availableProcessors( - _: NonNull, + _: JniEnv, _this: Reference, // java.lang.Runtime ) -> s4 { num_cpus::get() as s4 } -pub fn freeMemory(_: NonNull, _this: Reference /* java.lang.Runtime */) -> s8 { +pub fn freeMemory(_: JniEnv, _this: Reference /* java.lang.Runtime */) -> s8 { unimplemented!("java.lang.Runtime#freeMemory") } -pub fn totalMemory(_: NonNull, _this: Reference /* java.lang.Runtime */) -> s8 { +pub fn totalMemory(_: JniEnv, _this: Reference /* java.lang.Runtime */) -> s8 { unimplemented!("java.lang.Runtime#totalMemory") } -pub fn maxMemory(_: NonNull, _this: Reference /* java.lang.Runtime */) -> s8 { +pub fn maxMemory(_: JniEnv, _this: Reference /* java.lang.Runtime */) -> s8 { // TODO: Xmx s8::MAX } -pub fn gc(_: NonNull, _this: Reference /* java.lang.Runtime */) { +pub fn gc(_: JniEnv, _this: Reference /* java.lang.Runtime */) { unimplemented!("java.lang.Runtime#gc") } diff --git a/runtime/src/native/java/lang/StackTraceElement.rs b/runtime/src/native/java/lang/StackTraceElement.rs index 9918818..038494a 100644 --- a/runtime/src/native/java/lang/StackTraceElement.rs +++ b/runtime/src/native/java/lang/StackTraceElement.rs @@ -96,7 +96,7 @@ fn fill_in_stack_trace(stacktrace_element: ClassInstanceRef, method: &Method, pc } pub fn initStackTraceElements( - env: NonNull, + env: JniEnv, class: &'static Class, elements: Reference, // java.lang.StackTraceElement[] x: Reference, // java.lang.Object @@ -107,7 +107,7 @@ pub fn initStackTraceElements( } if x.is_null() || elements.is_null() { - let thread = unsafe { &*JavaThread::for_env(env.as_ptr()) }; + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; throw!(thread, NullPointerException); } @@ -118,7 +118,7 @@ pub fn initStackTraceElements( }; if stack_trace_elements.len() != depth as usize { - let thread = unsafe { &*JavaThread::for_env(env.as_ptr()) }; + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; throw!(thread, IndexOutOfBoundsException); } @@ -147,7 +147,7 @@ pub fn initStackTraceElements( } pub fn initStackTraceElement( - _: NonNull, + _: JniEnv, class: &'static Class, _element: Reference, // java.lang.StackTraceElement _sfi: Reference, // java.lang.StackFrameInfo diff --git a/runtime/src/native/java/lang/StringUTF16.rs b/runtime/src/native/java/lang/StringUTF16.rs index b7f6386..4e7a3e3 100644 --- a/runtime/src/native/java/lang/StringUTF16.rs +++ b/runtime/src/native/java/lang/StringUTF16.rs @@ -8,6 +8,6 @@ use common::int_types::s4; include_generated!("native/java/lang/def/StringUTF16.definitions.rs"); -pub fn isBigEndian(_env: NonNull, _class: &'static Class) -> s4 { +pub fn isBigEndian(_env: JniEnv, _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 0df62e5..246787c 100644 --- a/runtime/src/native/java/lang/System.rs +++ b/runtime/src/native/java/lang/System.rs @@ -15,7 +15,7 @@ include_generated!("native/java/lang/def/System.registerNatives.rs"); include_generated!("native/java/lang/def/System.definitions.rs"); pub fn setIn0( - _: NonNull, + _: JniEnv, class: &'static Class, in_: Reference, // java.io.InputStream ) { @@ -26,7 +26,7 @@ pub fn setIn0( field.set_static_value(Operand::Reference(in_)); } pub fn setOut0( - _env: NonNull, + _env: JniEnv, class: &'static Class, out: Reference, // java.io.PrintStream ) { @@ -37,7 +37,7 @@ pub fn setOut0( field.set_static_value(Operand::Reference(out)); } pub fn setErr0( - _env: NonNull, + _env: JniEnv, class: &'static Class, err: Reference, // java.io.PrintStream ) { @@ -48,11 +48,11 @@ pub fn setErr0( field.set_static_value(Operand::Reference(err)); } -pub fn currentTimeMillis(_env: NonNull, _class: &'static Class) -> jlong { +pub fn currentTimeMillis(_env: JniEnv, _class: &'static Class) -> jlong { unimplemented!("System#currentTimeMillis") } -pub fn nanoTime(_env: NonNull, _class: &'static Class) -> jlong { +pub fn nanoTime(_env: JniEnv, _class: &'static Class) -> jlong { let time_nanos = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("current system time should not be before the UNIX epoch") @@ -62,7 +62,7 @@ pub fn nanoTime(_env: NonNull, _class: &'static Class) -> jlong { } pub fn arraycopy( - env: NonNull, + env: JniEnv, _class: &'static Class, src: Reference, // java.lang.Object src_pos: jint, @@ -71,7 +71,7 @@ pub fn arraycopy( length: jint, ) { if src.is_null() || dest.is_null() { - let _thread = unsafe { &*JavaThread::for_env(env.as_ptr()) }; + let _thread = unsafe { &*JavaThread::for_env(env.raw()) }; todo!("NullPointerException") } @@ -101,17 +101,13 @@ pub fn arraycopy( } pub fn identityHashCode( - env: NonNull, + env: JniEnv, _class: &'static Class, x: Reference, // java.lang.Object ) -> jint { crate::native::java::lang::Object::hashCode(env, x) } -pub fn mapLibraryName( - _env: NonNull, - _class: &'static Class, - _libname: Reference, -) -> Reference { +pub fn mapLibraryName(_env: JniEnv, _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 9efc0cb..9cff3d9 100644 --- a/runtime/src/native/java/lang/Thread.rs +++ b/runtime/src/native/java/lang/Thread.rs @@ -15,39 +15,39 @@ 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, _class: &'static Class) -> Reference /* java.lang.Object */ +pub fn findScopedValueBindings(_env: JniEnv, _class: &'static Class) -> Reference /* java.lang.Object */ { unimplemented!("java.lang.Thread#findScopedValueBindings"); } -pub fn currentCarrierThread(_env: NonNull, _class: &'static Class) -> Reference /* java.lang.Thread */ +pub fn currentCarrierThread(_env: JniEnv, _class: &'static Class) -> Reference /* java.lang.Thread */ { unimplemented!("java.lang.Thread#currentCarrierThread"); } -pub fn currentThread(env: NonNull, _class: &'static Class) -> Reference /* java.lang.Thread */ +pub fn currentThread(env: JniEnv, _class: &'static Class) -> Reference /* java.lang.Thread */ { unsafe { - let thread = JavaThread::for_env(env.as_ptr() as _); + let thread = JavaThread::for_env(env.raw() as _); (*thread).obj().expect("current thread should exist") } } pub fn setCurrentThread( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Thread _thread: Reference, // java.lang.Thread ) { unimplemented!("java.lang.Thread#setCurrentThread"); } -pub fn scopedValueCache(_env: NonNull, _class: &'static Class) -> Reference /* []java.lang.Object */ +pub fn scopedValueCache(_env: JniEnv, _class: &'static Class) -> Reference /* []java.lang.Object */ { unimplemented!("java.lang.Thread#scopedValueCache"); } pub fn setScopedValueCache( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _cache: Reference, // []java.lang.Object ) { @@ -55,23 +55,23 @@ pub fn setScopedValueCache( } pub fn ensureMaterializedForStackWalk( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _o: Reference, // java.lang.Object ) { unimplemented!("java.lang.Thread#ensureMaterializedForStackWalk"); } -pub fn yield0(_env: NonNull, _class: &'static Class) { +pub fn yield0(_env: JniEnv, _class: &'static Class) { std::thread::yield_now(); } // throws InterruptedException -pub fn sleepNanos0(_env: NonNull, _class: &'static Class, _nanos: jlong) { +pub fn sleepNanos0(_env: JniEnv, _class: &'static Class, _nanos: jlong) { unimplemented!("java.lang.Thread#sleepNanos0"); } -pub fn start0(_env: NonNull, this: Reference /* java.lang.Thread */) { +pub fn start0(_env: JniEnv, this: Reference /* java.lang.Thread */) { { if let Some(existing_thread) = ThreadPool::find_from_obj(this.clone()) { throw!(existing_thread, IllegalThreadStateException); @@ -96,7 +96,7 @@ pub fn start0(_env: NonNull, this: Reference /* java.lang.Thread */) { } pub fn holdsLock( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _obj: Reference, // java.lang.Object ) -> jboolean { @@ -104,7 +104,7 @@ pub fn holdsLock( } pub fn getStackTrace0( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Thread ) -> Reference /* java.lang.Object */ { @@ -112,7 +112,7 @@ pub fn getStackTrace0( } pub fn dumpThreads( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _threads: Reference, // []java.lang.Thread ) -> Reference /* [][]java.lang.StackTraceElement */ @@ -120,13 +120,13 @@ pub fn dumpThreads( unimplemented!("java.lang.Thread#dumpThreads"); } -pub fn getThreads(_env: NonNull, _class: &'static Class) -> Reference /* []java.lang.Thread */ +pub fn getThreads(_env: JniEnv, _class: &'static Class) -> Reference /* []java.lang.Thread */ { unimplemented!("java.lang.Thread#getThreads"); } pub fn setPriority0( - _env: NonNull, + _env: JniEnv, this: Reference, // java.lang.Thread new_priority: jint, ) { @@ -141,23 +141,23 @@ pub fn setPriority0( todo!("Set priority on JavaThread?") } -pub fn interrupt0(_env: NonNull, _this: Reference /* java.lang.Thread */) { +pub fn interrupt0(_env: JniEnv, _this: Reference /* java.lang.Thread */) { unimplemented!("java.lang.Thread#interrupt"); } -pub fn clearInterruptEvent(_env: NonNull, _class: &'static Class) { +pub fn clearInterruptEvent(_env: JniEnv, _class: &'static Class) { unimplemented!("java.lang.Thread#clearInterruptEvent"); } pub fn setNativeName( - _env: NonNull, + _env: JniEnv, _this: Reference, // java.lang.Thread _name: Reference, // java.lang.String ) { unimplemented!("java.lang.Thread#setNativeName"); } -pub fn getNextThreadIdOffset(_env: NonNull, _class: &'static Class) -> jlong { +pub fn getNextThreadIdOffset(_env: JniEnv, _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 44f86e1..1ae8cac 100644 --- a/runtime/src/native/java/lang/Throwable.rs +++ b/runtime/src/native/java/lang/Throwable.rs @@ -90,7 +90,7 @@ unsafe fn initialize() { } pub fn fillInStackTrace( - env: NonNull, + env: JniEnv, mut this: Reference, // java.lang.Throwable _dummy: s4, ) -> Reference /* java.lang.Throwable */ @@ -104,7 +104,7 @@ pub fn fillInStackTrace( let stack_trace_offset = crate::globals::fields::java_lang_Throwable::stackTrace_field_offset(); this.put_field_value0(stack_trace_offset, Operand::Reference(Reference::null())); - let current_thread = unsafe { &*JavaThread::for_env(env.as_ptr() as _) }; + let current_thread = unsafe { &*JavaThread::for_env(env.raw() as _) }; let this_class_instance = this.extract_class(); let this_class = this_class_instance.get().class(); diff --git a/runtime/src/native/java/lang/ref/Finalizer.rs b/runtime/src/native/java/lang/ref/Finalizer.rs index eb472f4..9f2ad18 100644 --- a/runtime/src/native/java/lang/ref/Finalizer.rs +++ b/runtime/src/native/java/lang/ref/Finalizer.rs @@ -8,12 +8,12 @@ use jni::sys::jboolean; include_generated!("native/java/lang/ref/def/Finalizer.definitions.rs"); -pub fn isFinalizationEnabled(_: NonNull, _class: &'static Class) -> jboolean { +pub fn isFinalizationEnabled(_: JniEnv, _class: &'static Class) -> jboolean { false // finalization is deprecated anyway } pub fn reportComplete( - _: NonNull, + _: JniEnv, _class: &'static Class, _finalizee: Reference, // java.lang.Object ) { diff --git a/runtime/src/native/java/lang/ref/Reference.rs b/runtime/src/native/java/lang/ref/Reference.rs index 89cdde4..736f00e 100644 --- a/runtime/src/native/java/lang/ref/Reference.rs +++ b/runtime/src/native/java/lang/ref/Reference.rs @@ -9,21 +9,21 @@ use jni::sys::jboolean; include_generated!("native/java/lang/ref/def/Reference.definitions.rs"); -pub fn getAndClearReferencePendingList(_: NonNull, _class: &'static Class) -> Reference /* java.lang.ref.Reference */ +pub fn getAndClearReferencePendingList(_: JniEnv, _class: &'static Class) -> Reference /* java.lang.ref.Reference */ { unimplemented!("java.lang.ref.Reference#getAndClearReferencePendingList") } -pub fn hasReferencePendingList(_: NonNull, _class: &'static Class) -> jboolean { +pub fn hasReferencePendingList(_: JniEnv, _class: &'static Class) -> jboolean { unimplemented!("java.lang.ref.Reference#hasReferencePendingList") } -pub fn waitForReferencePendingList(_: NonNull, _class: &'static Class) { +pub fn waitForReferencePendingList(_: JniEnv, _class: &'static Class) { unimplemented!("java.lang.ref.Reference#waitForReferencePendingList") } pub fn refersTo0( - _: NonNull, + _: JniEnv, this: Reference, // java.lang.ref.Reference o: Reference, // java.lang.Object ) -> jboolean { @@ -34,6 +34,6 @@ pub fn refersTo0( referent.expect_reference() == o } -pub fn clear0(_: NonNull, _this: Reference /* java.lang.ref.Reference */) { +pub fn clear0(_: JniEnv, _this: Reference /* java.lang.ref.Reference */) { unimplemented!("java.lang.ref.Reference#clear0") } diff --git a/runtime/src/native/java/security/AccessController.rs b/runtime/src/native/java/security/AccessController.rs index 3a27ef8..ee95d9b 100644 --- a/runtime/src/native/java/security/AccessController.rs +++ b/runtime/src/native/java/security/AccessController.rs @@ -8,7 +8,7 @@ use jni::env::JniEnv; include_generated!("native/java/security/def/AccessController.definitions.rs"); pub fn getProtectionDomain( - _env: NonNull, + _env: JniEnv, _this_class: &'static Class, _class: Reference, // java.lang.Class ) -> Reference /* java.security.ProtectionDomain */ { @@ -16,24 +16,21 @@ pub fn getProtectionDomain( } pub fn ensureMaterializedForStackWalk( - _env: NonNull, + _env: JniEnv, _this_class: &'static Class, _class: Reference, // java.lang.Object ) { unimplemented!("java.security.AccessController#ensureMaterializedForStackWalk") } -pub fn getStackAccessControlContext(_env: NonNull, _class: &'static Class) -> Reference /* java.security.AccessControlContext */ +pub fn getStackAccessControlContext(_env: JniEnv, _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, - _class: &'static Class, -) -> Reference /* java.security.AccessControlContext */ +pub fn getInheritedAccessControlContext(_env: JniEnv, _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 3db4a86..6d0f9a1 100644 --- a/runtime/src/native/jdk/internal/loader/BootLoader.rs +++ b/runtime/src/native/jdk/internal/loader/BootLoader.rs @@ -1,4 +1,4 @@ -use crate::classpath::classloader::ClassLoader; +use crate::classpath::loader::ClassLoader; use crate::modules::Module; use crate::objects::class::Class; use crate::objects::instance::Instance; @@ -6,15 +6,13 @@ use crate::objects::reference::Reference; use crate::thread::exceptions::{handle_exception, throw}; use crate::thread::JavaThread; -use std::ptr::NonNull; - use ::jni::env::JniEnv; 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, _class: &'static Class) -> Reference /* String[] */ +pub fn getSystemPackageNames(_env: JniEnv, _class: &'static Class) -> Reference /* String[] */ { unimplemented!("jdk.internal.loader.BootLoader#getSystemPackageNames") } @@ -25,7 +23,7 @@ pub fn getSystemPackageNames(_env: NonNull, _class: &'static Class) -> R /// The location may be a module from the runtime image or exploded image, or from the boot class /// append path (i.e. -Xbootclasspath/a or BOOT-CLASS-PATH attribute specified in java agent). pub fn getSystemPackageLocation( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _name: Reference, // java.lang.String ) -> Reference /* java.lang.String */ { @@ -39,11 +37,11 @@ pub fn getSystemPackageLocation( /// * Module is not an instance or subclass of j.l.r.Module /// * Module is not loaded by the bootLoader pub fn setBootLoaderUnnamedModule0( - env: NonNull, + env: JniEnv, _class: &'static Class, module: Reference, // java.lang.Module ) { - let thread = unsafe { &*JavaThread::for_env(env.as_ptr()) }; + let thread = unsafe { &*JavaThread::for_env(env.raw()) }; let module_entry_result = Module::unnamed(module.clone()); let module_entry = handle_exception!(thread, module_entry_result); diff --git a/runtime/src/native/jdk/internal/loader/NativeLibraries.rs b/runtime/src/native/jdk/internal/loader/NativeLibraries.rs index 06c4c3e..f5d0da4 100644 --- a/runtime/src/native/jdk/internal/loader/NativeLibraries.rs +++ b/runtime/src/native/jdk/internal/loader/NativeLibraries.rs @@ -1,15 +1,13 @@ use crate::objects::class::Class; use crate::objects::reference::Reference; -use std::ptr::NonNull; - use ::jni::env::JniEnv; use ::jni::sys::{jboolean, jlong}; include_generated!("native/jdk/internal/loader/def/NativeLibraries.definitions.rs"); pub fn load( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _impl_: Reference, // jdk.internal.loader.NativeLibraries$NativeLibraryImpl _name: Reference, // java.lang.String @@ -20,7 +18,7 @@ pub fn load( } pub fn unload( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _name: Reference, // java.lang.String _is_builtin: jboolean, @@ -30,7 +28,7 @@ pub fn unload( } pub fn findBuiltinLib( - _env: NonNull, + _env: JniEnv, _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 8a9139b..b3eb77a 100644 --- a/runtime/src/native/jdk/internal/misc/CDS.rs +++ b/runtime/src/native/jdk/internal/misc/CDS.rs @@ -8,7 +8,7 @@ use ::jni::sys::{jint, jlong}; include_generated!("native/jdk/internal/misc/def/CDS.definitions.rs"); -pub fn getCDSConfigStatus(_: NonNull, _class: &'static Class) -> jint { +pub fn getCDSConfigStatus(_: JniEnv, _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; @@ -17,16 +17,16 @@ pub fn getCDSConfigStatus(_: NonNull, _class: &'static Class) -> jint { 0 } -pub fn logLambdaFormInvoker(_: NonNull, _class: &'static Class, _line: Reference) { +pub fn logLambdaFormInvoker(_: JniEnv, _class: &'static Class, _line: Reference) { unimplemented!("jdk.internal.misc.CDS#logLambdaFormInvoker") } -pub fn initializeFromArchive(_: NonNull, _this_class: &'static Class, _class: Reference) { +pub fn initializeFromArchive(_: JniEnv, _this_class: &'static Class, _class: Reference) { // TODO } pub fn defineArchivedModules( - _: NonNull, + _: JniEnv, _class: &'static Class, _platform_loader: Reference, _system_loader: Reference, @@ -34,18 +34,14 @@ pub fn defineArchivedModules( unimplemented!("jdk.internal.misc.CDS#defineArchivedModules") } -pub fn getRandomSeedForDumping(_: NonNull, _class: &'static Class) -> jlong { +pub fn getRandomSeedForDumping(_: JniEnv, _class: &'static Class) -> jlong { // TODO: https://github.com/openjdk/jdk/blob/af564e46b006fcd57ec7391cd1438b3b9311b1d6/src/hotspot/share/prims/jvm.cpp#L3696 0 } -pub fn dumpClassList(_: NonNull, _class: &'static Class, _list_file_name: Reference) { +pub fn dumpClassList(_: JniEnv, _class: &'static Class, _list_file_name: Reference) { unimplemented!("jdk.internal.misc.CDS#dumpClassList") } -pub fn dumpDynamicArchive( - _: NonNull, - _class: &'static Class, - _archive_file_name: Reference, -) { +pub fn dumpDynamicArchive(_: JniEnv, _class: &'static Class, _archive_file_name: Reference) { unimplemented!("jdk.internal.misc.CDS#dumpDynamicArchive") } diff --git a/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs b/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs index 63fbbe7..71a407b 100644 --- a/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs +++ b/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs @@ -8,7 +8,7 @@ include_generated!("native/jdk/internal/misc/def/ScopedMemoryAccess.definitions. include_generated!("native/jdk/internal/misc/def/ScopedMemoryAccess.registerNatives.rs"); pub fn closeScope0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.ScopedMemoryAccess _session: Reference, // jdk.internal.foreign.MemorySessionImpl _error: Reference, // jdk.internal.misc.ScopedMemoryAccess.ScopedAccessError diff --git a/runtime/src/native/jdk/internal/misc/Signal.rs b/runtime/src/native/jdk/internal/misc/Signal.rs index 1f09a62..d1c3e29 100644 --- a/runtime/src/native/jdk/internal/misc/Signal.rs +++ b/runtime/src/native/jdk/internal/misc/Signal.rs @@ -2,15 +2,13 @@ use crate::objects::class::Class; use crate::objects::reference::Reference; use crate::string_interner::StringInterner; -use std::ptr::NonNull; - use ::jni::env::JniEnv; use ::jni::sys::{jint, jlong}; include_generated!("native/jdk/internal/misc/def/Signal.definitions.rs"); pub fn findSignal0( - _: NonNull, + _: JniEnv, _class: &'static Class, sig_name: Reference, // java.lang.String ) -> jint { @@ -23,7 +21,7 @@ pub fn findSignal0( } } -pub fn handle0(_: NonNull, _class: &'static Class, sig: jint, native_h: jlong) -> jlong { +pub fn handle0(_: JniEnv, _class: &'static Class, sig: jint, native_h: jlong) -> jlong { let signal = platform::Signal::from(sig); if !signal.registration_allowed() { @@ -48,6 +46,6 @@ pub fn handle0(_: NonNull, _class: &'static Class, sig: jint, native_h: old.as_usize() as jlong } -pub fn raise0(_: NonNull, _class: &'static Class, _sig: jint) { +pub fn raise0(_: JniEnv, _class: &'static Class, _sig: jint) { unimplemented!("jdk.internal.misc.Signal#raise0"); } diff --git a/runtime/src/native/jdk/internal/misc/Unsafe.rs b/runtime/src/native/jdk/internal/misc/Unsafe.rs index ac500c4..6107c7c 100644 --- a/runtime/src/native/jdk/internal/misc/Unsafe.rs +++ b/runtime/src/native/jdk/internal/misc/Unsafe.rs @@ -6,7 +6,6 @@ use crate::string_interner::StringInterner; use crate::thread::JavaThread; use std::marker::PhantomData; -use std::ptr::NonNull; use std::sync::atomic::{ AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicU16, Ordering, }; @@ -294,7 +293,7 @@ unsafe_ops! { } pub fn getUncompressedObject( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _address: jlong, ) -> Reference { @@ -302,27 +301,27 @@ pub fn getUncompressedObject( } pub fn writeback0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _address: jlong, ) { unimplemented!("jdk.internal.misc.Unsafe#writeback0") } pub fn writebackPreSync0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe ) { unimplemented!("jdk.internal.misc.Unsafe#writebackPreSync0") } pub fn writebackPostSync0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe ) { unimplemented!("jdk.internal.misc.Unsafe#writebackPostSync0") } pub fn defineClass0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _name: Reference, // java.lang.String _bytes: Reference, // [B @@ -336,7 +335,7 @@ pub fn defineClass0( // throws InstantiationException pub fn allocateInstance( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _class: Reference, // java.lang.Class ) -> Reference { @@ -344,7 +343,7 @@ pub fn allocateInstance( } pub fn throwException( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _exception: Reference, // java.lang.Throwable ) { @@ -352,7 +351,7 @@ pub fn throwException( } pub fn compareAndSetInt( - env: NonNull, + env: JniEnv, this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -363,7 +362,7 @@ pub fn compareAndSetInt( } pub fn compareAndExchangeInt( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -383,7 +382,7 @@ pub fn compareAndExchangeInt( } pub fn compareAndSetLong( - env: NonNull, + env: JniEnv, this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -394,7 +393,7 @@ pub fn compareAndSetLong( } pub fn compareAndExchangeLong( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -415,7 +414,7 @@ pub fn compareAndExchangeLong( } pub fn compareAndSetReference( - env: NonNull, + env: JniEnv, this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -433,7 +432,7 @@ pub fn compareAndSetReference( } pub fn compareAndExchangeReference( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -474,7 +473,7 @@ pub fn compareAndExchangeReference( } pub fn getReference( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -503,7 +502,7 @@ pub fn getReference( } pub fn putReference( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -533,7 +532,7 @@ pub fn putReference( } pub fn getReferenceVolatile( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -543,7 +542,7 @@ pub fn getReferenceVolatile( } pub fn putReferenceVolatile( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // java.lang.Object offset: jlong, @@ -559,7 +558,7 @@ macro_rules! get_put_methods { $( paste::paste! { pub fn []( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong @@ -569,7 +568,7 @@ macro_rules! get_put_methods { } pub fn []( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -580,7 +579,7 @@ macro_rules! get_put_methods { } pub fn []( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong @@ -590,7 +589,7 @@ macro_rules! get_put_methods { } pub fn []( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe object: Reference, // Object offset: jlong, @@ -607,7 +606,7 @@ macro_rules! get_put_methods { get_put_methods! { (boolean; AtomicBool), (byte; AtomicI8), (short; AtomicI16), (char; AtomicU16), (int; AtomicI32), (long; AtomicI64), (float; AtomicF32), (double; AtomicF64) } pub fn unpark( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _thread: Reference, // Object ) { @@ -615,7 +614,7 @@ pub fn unpark( } pub fn park( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _is_absolute: bool, _time: jlong, @@ -624,14 +623,14 @@ pub fn park( } pub fn fullFence( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe ) { platform::arch::ordering::fence(); } pub fn allocateMemory0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _bytes: jlong, ) -> jlong { @@ -639,7 +638,7 @@ pub fn allocateMemory0( } pub fn reallocateMemory0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _address: jlong, _bytes: jlong, @@ -648,7 +647,7 @@ pub fn reallocateMemory0( } pub fn freeMemory0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _address: jlong, ) { @@ -656,7 +655,7 @@ pub fn freeMemory0( } pub fn setMemory0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _object: Reference, // Object _offset: jlong, @@ -667,7 +666,7 @@ pub fn setMemory0( } pub fn copyMemory0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _src_base: Reference, // Object _src_offset: jlong, @@ -679,7 +678,7 @@ pub fn copyMemory0( } pub fn copySwapMemory0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _src_base: Reference, // Object _src_offset: jlong, @@ -692,7 +691,7 @@ pub fn copySwapMemory0( } pub fn objectFieldOffset0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _field: Reference, // java.lang.reflect.Field ) -> jlong { @@ -700,7 +699,7 @@ pub fn objectFieldOffset0( } pub fn objectFieldOffset1( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe class: Reference, // java.lang.Class name: Reference, // String @@ -728,7 +727,7 @@ pub fn objectFieldOffset1( } pub fn staticFieldOffset0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _field: Reference, // java.lang.reflect.Field ) -> jlong { @@ -736,25 +735,25 @@ pub fn staticFieldOffset0( } pub fn staticFieldBase0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _field: Reference, // java.lang.reflect.Field ) -> Reference /* java.lang.Object */ { unimplemented!("jdk.internal.misc.Unsafe#staticFieldBase0") } pub fn shouldBeInitialized0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _class: Reference, // java.lang.Class ) -> bool { unimplemented!("jdk.internal.misc.Unsafe#shouldBeInitialized0") } pub fn ensureClassInitialized0( - env: NonNull, + env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe class: Reference, // java.lang.Class ) { - let current_thread = unsafe { &mut *JavaThread::for_env(env.as_ptr() as _) }; + let current_thread = unsafe { &mut *JavaThread::for_env(env.raw()) }; let mirror = class.extract_mirror(); let target_class = mirror.get().target_class(); @@ -766,7 +765,7 @@ pub fn ensureClassInitialized0( } } pub fn arrayBaseOffset0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe array_class: Reference, // java.lang.Class ) -> jint { @@ -778,7 +777,7 @@ pub fn arrayBaseOffset0( 0 } pub fn arrayIndexScale0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe array_class: Reference, // java.lang.Class ) -> jint { @@ -790,7 +789,7 @@ pub fn arrayIndexScale0( 1 } pub fn getLoadAverage0( - _env: NonNull, + _env: JniEnv, _this: Reference, // jdk.internal.misc.Unsafe _loadavg: Reference, // [D _nelems: jint, diff --git a/runtime/src/native/jdk/internal/misc/VM.rs b/runtime/src/native/jdk/internal/misc/VM.rs index 73b5981..1fb3b85 100644 --- a/runtime/src/native/jdk/internal/misc/VM.rs +++ b/runtime/src/native/jdk/internal/misc/VM.rs @@ -9,29 +9,29 @@ use common::int_types::s8; include_generated!("native/jdk/internal/misc/def/VM.definitions.rs"); -pub fn latestUserDefinedLoader0(_env: NonNull, _class: &'static Class) -> Reference /* java.lang.ClassLoader */ +pub fn latestUserDefinedLoader0(_env: JniEnv, _class: &'static Class) -> Reference /* java.lang.ClassLoader */ { unimplemented!("jdk.internal.misc.VM#latestUserDefinedLoader0") } -pub fn getuid(_env: NonNull, _class: &'static Class) -> s8 { +pub fn getuid(_env: JniEnv, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#getuid") } -pub fn geteuid(_env: NonNull, _class: &'static Class) -> s8 { +pub fn geteuid(_env: JniEnv, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#geteuid") } -pub fn getgid(_env: NonNull, _class: &'static Class) -> s8 { +pub fn getgid(_env: JniEnv, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#getgid") } -pub fn getegid(_env: NonNull, _class: &'static Class) -> s8 { +pub fn getegid(_env: JniEnv, _class: &'static Class) -> s8 { unimplemented!("jdk.internal.misc.VM#getegid") } -pub fn getNanoTimeAdjustment(_env: NonNull, _class: &'static Class, _offset: s8) -> s8 { +pub fn getNanoTimeAdjustment(_env: JniEnv, _class: &'static Class, _offset: s8) -> s8 { unimplemented!("jdk.internal.misc.VM#getNanoTimeAdjustment") } -pub fn getRuntimeArguments(_env: NonNull, _class: &'static Class) -> Reference /* String[] */ +pub fn getRuntimeArguments(_env: JniEnv, _class: &'static Class) -> Reference /* String[] */ { unimplemented!("jdk.internal.misc.VM#getRuntimeArguments") } -pub fn initialize(_env: NonNull, _class: &'static Class) { +pub fn initialize(_env: JniEnv, _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 c46418a..88f988d 100644 --- a/runtime/src/native/jdk/internal/reflect/Reflection.rs +++ b/runtime/src/native/jdk/internal/reflect/Reflection.rs @@ -2,16 +2,14 @@ use crate::objects::class::Class; use crate::objects::reference::Reference; use crate::thread::JavaThread; -use std::ptr::NonNull; - use ::jni::env::JniEnv; 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, _class: &'static Class) -> Reference { - let current_thread = unsafe { &*JavaThread::for_env(env.as_ptr() as _) }; +pub fn getCallerClass(env: JniEnv, _class: &'static Class) -> Reference { + let current_thread = unsafe { &*JavaThread::for_env(env.raw() as _) }; // The call stack at this point looks something like this: // @@ -51,16 +49,12 @@ pub fn getCallerClass(env: NonNull, _class: &'static Class) -> Reference Reference::null() } -pub fn getClassAccessFlags( - _env: NonNull, - _this_class: &'static Class, - _class: Reference, -) -> jint { +pub fn getClassAccessFlags(_env: JniEnv, _this_class: &'static Class, _class: Reference) -> jint { unimplemented!("jdk.internal.reflect.Reflection#getClassAccessFlags") } pub fn areNestMates( - _env: NonNull, + _env: JniEnv, _class: &'static Class, _current_class: Reference, _member_class: Reference, diff --git a/runtime/src/native/jdk/internal/util/SystemProps.rs b/runtime/src/native/jdk/internal/util/SystemProps.rs index 409844c..8b2a264 100644 --- a/runtime/src/native/jdk/internal/util/SystemProps.rs +++ b/runtime/src/native/jdk/internal/util/SystemProps.rs @@ -5,8 +5,6 @@ pub mod Raw { use crate::objects::reference::Reference; use crate::string_interner::StringInterner; - use std::ptr::NonNull; - use ::jni::env::JniEnv; use common::traits::PtrType; use instructions::Operand; @@ -20,7 +18,7 @@ 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, _class: &'static Class) -> Reference /* [Ljava/lang/String; */ + pub fn vmProperties(_env: JniEnv, _class: &'static Class) -> Reference /* [Ljava/lang/String; */ { macro_rules! store_properties { ($prop_array:ident; $($key:literal => $value:expr),+ $(,)?) => { @@ -60,7 +58,7 @@ pub mod Raw { Reference::array(prop_array) } - pub fn platformProperties(_env: NonNull, _class: &'static Class) -> Reference /* [Ljava/lang/String; */ + pub fn platformProperties(_env: JniEnv, _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 bd0555f..0e05aaa 100644 --- a/runtime/src/native/jni/class.rs +++ b/runtime/src/native/jni/class.rs @@ -1,5 +1,5 @@ use super::{classref_from_jclass, IntoJni}; -use crate::classpath::classloader::ClassLoader; +use crate::classpath::loader::ClassLoader; use crate::objects::class::Class; use crate::thread::JavaThread; diff --git a/runtime/src/native/jni/exceptions.rs b/runtime/src/native/jni/exceptions.rs index a06e004..6665fe6 100644 --- a/runtime/src/native/jni/exceptions.rs +++ b/runtime/src/native/jni/exceptions.rs @@ -1,3 +1,5 @@ +use crate::thread::JavaThread; + use core::ffi::c_char; use jni::sys::{jboolean, jclass, jint, jthrowable, JNIEnv}; @@ -33,5 +35,8 @@ pub extern "system" fn FatalError(env: *mut JNIEnv, msg: *const c_char) -> ! { #[no_mangle] pub extern "system" fn ExceptionCheck(env: *mut JNIEnv) -> jboolean { - unimplemented!("jni::ExceptionCheck"); + let thread = JavaThread::current(); + assert_eq!(thread.env().raw(), env); + + thread.has_pending_exception() } diff --git a/runtime/src/native/jni/invocation_api/mod.rs b/runtime/src/native/jni/invocation_api/mod.rs index a00fe8f..386e3c6 100644 --- a/runtime/src/native/jni/invocation_api/mod.rs +++ b/runtime/src/native/jni/invocation_api/mod.rs @@ -8,6 +8,7 @@ use crate::thread::{JavaThread, JavaThreadBuilder}; use core::ffi::c_void; use std::ffi::CStr; +use jni::error::JniError; use jni::java_vm::JavaVm; use jni::sys::{ jint, jsize, JNIInvokeInterface_, JNINativeInterface_, JavaVM, JNI_EVERSION, JNI_OK, @@ -69,15 +70,27 @@ pub extern "system" fn JNI_CreateJavaVM( return jni::sys::JNI_EVERSION; } - let vm = initialization::create_java_vm(Some(args)); + let vm; + match initialization::create_java_vm(Some(args)) { + Ok(java_vm) => vm = java_vm, + Err(e) => { + if let JniError::ExceptionThrown = e { + JavaThread::current().throw_pending_exception(true); + } + + // All errors result in an abort + std::process::abort(); + }, + } + let current_thread = JavaThread::current(); - let env = current_thread.env().as_ptr(); + let env = current_thread.env(); unsafe { *pvm = vm.raw() as _; - *penv = (*env).raw() as _; + *penv = env.raw() as _; } - unimplemented!("jni::JNI_CreateJavaVM") + JNI_OK } #[no_mangle] @@ -97,9 +110,9 @@ fn attach_current_thread_impl( ) -> jint { // We're already attached if let Some(thread) = JavaThread::current_opt() { - let env = thread.env().as_ptr(); + let env = thread.env(); unsafe { - *penv = (*env).raw() as _; + *penv = env.raw() as _; } return JNI_OK; } diff --git a/runtime/src/native/method.rs b/runtime/src/native/method.rs index 6b4674d..33823d8 100644 --- a/runtime/src/native/method.rs +++ b/runtime/src/native/method.rs @@ -5,7 +5,6 @@ use crate::stack::local_stack::LocalStack; use std::collections::HashMap; use std::fmt::Debug; -use std::ptr::NonNull; use std::sync::{LazyLock, RwLock}; use ::jni::env::JniEnv; @@ -32,8 +31,8 @@ impl Debug for NativeMethodDef { } pub type NativeReturn = Option>; -pub type NativeStaticMethodPtr = fn(NonNull, &'static Class, LocalStack) -> NativeReturn; -pub type NativeNonStaticMethodPtr = fn(NonNull, LocalStack) -> NativeReturn; +pub type NativeStaticMethodPtr = fn(JniEnv, &'static Class, LocalStack) -> NativeReturn; +pub type NativeNonStaticMethodPtr = fn(JniEnv, LocalStack) -> NativeReturn; #[derive(Copy, Clone)] union NativeMethodPtrInner { diff --git a/runtime/src/objects/array.rs b/runtime/src/objects/array.rs index b02ce4c..cbe1316 100644 --- a/runtime/src/objects/array.rs +++ b/runtime/src/objects/array.rs @@ -1,11 +1,12 @@ use super::class::Class; use super::instance::{CloneableInstance, Header}; use super::reference::{ArrayInstanceRef, Reference}; -use crate::classpath::classloader::ClassLoader; +use crate::classpath::loader::ClassLoader; use std::fmt::{Debug, Formatter}; use std::ptr::NonNull; +use crate::thread::exceptions::Throws; use common::box_slice; use common::int_types::{s1, s2, s4, s8, u1, u2}; use common::traits::PtrType; @@ -84,6 +85,62 @@ impl ArrayInstance { }) } + // https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-6.html#jvms-6.5.multianewarray + pub fn new_multidimensional( + counts: impl IntoIterator, + array_class: &'static Class, + ) -> Throws { + fn inner( + parent: &mut ArrayInstance, + parent_count: s4, + counts: &mut impl Iterator, + array_class: &'static Class, + ) -> Throws<()> { + let Some(count) = counts.next() else { + return Throws::Ok(()); + }; + + let component_name = array_class.array_component_name(); + let component_class = array_class + .loader() + .load(component_name) + .expect("component classes must exist"); + for i in 0..parent_count { + let instance = ArrayInstance::new_reference(count, array_class); + inner(instance.get_mut(), parent_count, counts, component_class)?; + parent.store(i, Operand::Reference(Reference::array(instance))); + } + + Throws::Ok(()) + } + + assert!( + array_class.is_array(), + "multi-dimensional arrays must have array component types" + ); + + let mut counts = counts.into_iter(); + + let initial_count = counts + .next() + .expect("multi-dimensional arrays must have at least one element"); + let initial_instance = Self::new_reference(initial_count, array_class); + + let component_name = array_class.array_component_name(); + let component_class = array_class + .loader() + .load(component_name) + .expect("component classes must exist"); + inner( + initial_instance.get_mut(), + initial_count, + &mut counts, + component_class, + )?; + + Throws::Ok(initial_instance) + } + pub fn get(&self, index: s4) -> Operand { if index.is_negative() || index as usize > self.elements.element_count() { panic!("ArrayIndexOutOfBoundsException"); // TODO diff --git a/runtime/src/objects/class/mod.rs b/runtime/src/objects/class/mod.rs index 7053a5e..f987295 100644 --- a/runtime/src/objects/class/mod.rs +++ b/runtime/src/objects/class/mod.rs @@ -7,9 +7,10 @@ use super::field::Field; use super::method::Method; use super::mirror::MirrorInstance; use super::vtable::VTable; -use crate::classpath::classloader::ClassLoader; +use crate::classpath::loader::ClassLoader; use crate::error::RuntimeError; use crate::globals::PRIMITIVES; +use crate::modules::{Module, Package}; use crate::objects::constant_pool::cp_types; use crate::objects::reference::{MirrorInstanceRef, Reference}; use crate::thread::JavaThread; @@ -19,8 +20,8 @@ use std::fmt::{Debug, Formatter}; use std::mem::MaybeUninit; use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::Arc; +use std::{mem, ptr}; -use crate::modules::{Module, Package}; use classfile::accessflags::ClassAccessFlags; use classfile::{ClassFile, FieldType, MethodInfo}; use common::box_slice; @@ -38,8 +39,8 @@ struct MiscCache { } struct FieldContainer { - /// A list of all fields, including static - fields: UnsafeCell>, + /// A list of all fields, including static. Only ever uninit during field injection. + fields: UnsafeCell>>, /// All static field slots /// /// This needs to be scaled to the `fields` field, in that index 0 of this array relates @@ -55,7 +56,7 @@ impl FieldContainer { /// Used as the field container for arrays, as they have no instance fields. fn null() -> Self { Self { - fields: UnsafeCell::new(box_slice![]), + fields: UnsafeCell::new(MaybeUninit::new(box_slice![])), static_field_slots: box_slice![], instance_field_count: UnsafeCell::new(0), } @@ -63,21 +64,24 @@ impl FieldContainer { fn new(static_field_slots: Box<[UnsafeCell>]>) -> Self { Self { - fields: UnsafeCell::new(box_slice![]), + fields: UnsafeCell::new(MaybeUninit::new(box_slice![])), static_field_slots, instance_field_count: UnsafeCell::new(0), } } fn fields(&self) -> impl Iterator { - let fields = unsafe { &*self.fields.get() }; + let fields = unsafe { (&*self.fields.get()).assume_init_ref() }; fields.iter().copied() } // This is only ever used in class loading fn set_fields(&self, new: Vec<&'static Field>) { let fields = self.fields.get(); - let old = core::mem::replace(unsafe { &mut *fields }, new.into_boxed_slice()); + let old = mem::replace( + unsafe { &mut *fields }, + MaybeUninit::new(new.into_boxed_slice()), + ); drop(old); } @@ -86,7 +90,7 @@ impl FieldContainer { /// See [`Class::set_static_field`] unsafe fn set_static_field(&self, index: usize, value: Operand) { let field = &self.static_field_slots[index]; - let old = core::mem::replace(unsafe { &mut *field.get() }, value); + let old = mem::replace(unsafe { &mut *field.get() }, value); drop(old); } @@ -105,7 +109,7 @@ impl FieldContainer { unsafe { *self.instance_field_count.get() } } - // This is only ever used in class loading + // This is only ever used in class loading and field injection fn set_instance_field_count(&self, value: u4) { unsafe { *self.instance_field_count.get() = value; @@ -337,7 +341,7 @@ impl Class { package } - pub fn module(&self) -> Arc { + pub fn module(&self) -> &'static Module { let bootstrap_loader = ClassLoader::bootstrap(); if !bootstrap_loader.java_base().has_obj() { // Assume we are early in VM initialization, where `java.base` isn't a real module yet. @@ -449,6 +453,53 @@ impl Class { self.field_container.set_static_field(index, value); } } + + /// Inject a set of fields into this class + /// + /// This can only ever be called once, and is **NEVER** to be used outside of initialization. + /// + /// This allows us to store extra information in objects as necessary, such as a [`Module`] pointer + /// in a `java.lang.Module` object. + pub unsafe fn inject_fields( + &self, + fields: impl IntoIterator, + field_count: usize, + ) { + assert!(field_count > 0, "field injection requires at least 1 field"); + + let old_fields_ptr = self.field_container.fields.get(); + let old_fields = unsafe { + let old_fields = ptr::replace(old_fields_ptr, MaybeUninit::uninit()); + old_fields.assume_init() + }; + + let max_instance_index = + old_fields + .iter() + .fold(0, |a, b| if b.is_static() { a } else { a.max(b.index()) }); + + let expected_len = old_fields.len() + field_count; + let mut new_fields = Vec::with_capacity(expected_len); + new_fields.extend(old_fields); + + for (idx, field) in fields.into_iter().enumerate() { + assert!(!field.is_static()); + field.set_index(max_instance_index + idx + 1); + new_fields.push(field); + } + + assert_eq!(new_fields.len(), expected_len); + + let new_fields = MaybeUninit::new(new_fields.into_boxed_slice()); + unsafe { + ptr::write(old_fields_ptr, new_fields); + } + + // + 1 as `max_instance_index` is the index of the *last* instance field, not of the first + // field we inject. + self.field_container + .set_instance_field_count((max_instance_index + 1 + field_count) as u32); + } } // Flags diff --git a/runtime/src/objects/class/spec.rs b/runtime/src/objects/class/spec.rs index 61090cb..245cc1c 100644 --- a/runtime/src/objects/class/spec.rs +++ b/runtime/src/objects/class/spec.rs @@ -171,25 +171,16 @@ impl Class { // https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html#jvms-5.4.3.3 #[tracing::instrument(skip_all)] - pub fn resolve_method<'a>( - class: &'static Class, - interface_method: bool, // TODO: Should just call `resolve_interface_method` directly - name: Symbol, - descriptor: Symbol, - ) -> Option<&'static Method> { - if interface_method { - return class.resolve_interface_method(name, descriptor); - } - + pub fn resolve_method<'a>(&self, name: Symbol, descriptor: Symbol) -> Option<&'static Method> { // When resolving a method reference: // 1. If C is an interface, method resolution throws an IncompatibleClassChangeError. - if class.is_interface() { + if self.is_interface() { panic!("IncompatibleClassChangeError"); // TODO } // 2. Otherwise, method resolution attempts to locate the referenced method in C and its superclasses: - if let ret @ Some(_) = class.resolve_method_step_two(name, descriptor) { + if let ret @ Some(_) = self.resolve_method_step_two(name, descriptor) { return ret; } @@ -197,13 +188,13 @@ impl Class { // 3.1. If the maximally-specific superinterface methods of C for the name and descriptor specified by the method reference include // exactly one method that does not have its ACC_ABSTRACT flag set, then this method is chosen and method lookup succeeds. - if let Some(method) = class.resolve_method_in_superinterfaces(name, descriptor, true) { + if let Some(method) = self.resolve_method_in_superinterfaces(name, descriptor, true) { return Some(method); } // 3.2. Otherwise, if any superinterface of C declares a method with the name and descriptor specified by the method reference that // has neither its ACC_PRIVATE flag nor its ACC_STATIC flag set, one of these is arbitrarily chosen and method lookup succeeds. - if let Some(method) = class.resolve_method_in_superinterfaces(name, descriptor, false) { + if let Some(method) = self.resolve_method_in_superinterfaces(name, descriptor, false) { return Some(method); } @@ -251,7 +242,7 @@ impl Class { } // https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html#jvms-5.4.3.4 - fn resolve_interface_method( + pub fn resolve_interface_method( &self, method_name: Symbol, descriptor: Symbol, @@ -305,9 +296,19 @@ impl Class { &self, method_name: Symbol, descriptor: Symbol, - // TODO: Deal with maximally-specific check (§5.4.3.3) maximally_specific: bool, ) -> Option<&'static Method> { + // A maximally-specific superinterface method of a class or interface C for a particular method name and descriptor is any method for which all of the following are true: + // + // The method is declared in a superinterface (direct or indirect) of C. + // + // The method is declared with the specified name and descriptor. + // + // The method has neither its ACC_PRIVATE flag nor its ACC_STATIC flag set. + // + // Where the method is declared in interface I, there exists no other maximally-specific superinterface method of C with the specified name and descriptor that is declared in a subinterface of I. + let mut maximally_specific_definition = None; + for interface in &self.interfaces { if let Some(method) = interface.resolve_method_in_superinterfaces( method_name, @@ -327,10 +328,27 @@ impl Class { continue; } + if maximally_specific { + if method.is_abstract() { + continue; + } + + match maximally_specific_definition { + Some(_) => maximally_specific_definition = None, + None => maximally_specific_definition = Some(method), + } + + continue; + } + return Some(method); } } + if maximally_specific { + return maximally_specific_definition; + } + None } @@ -403,7 +421,7 @@ impl Class { .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { - self.set_static_field(field.idx, value); + self.set_static_field(field.index(), value); } }, FieldType::Double => { @@ -412,7 +430,7 @@ impl Class { .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { - self.set_static_field(field.idx, value); + self.set_static_field(field.index(), value); } }, FieldType::Float => { @@ -421,7 +439,7 @@ impl Class { .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { - self.set_static_field(field.idx, value); + self.set_static_field(field.index(), value); } }, FieldType::Long => { @@ -430,7 +448,7 @@ impl Class { .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { - self.set_static_field(field.idx, value); + self.set_static_field(field.index(), value); } }, FieldType::Object(ref obj) if &**obj == b"java/lang/String" => { @@ -440,7 +458,7 @@ impl Class { let string_instance = StringInterner::intern_symbol(raw_string); let value = Operand::Reference(Reference::class(string_instance)); unsafe { - self.set_static_field(field.idx, value); + self.set_static_field(field.index(), value); } }, _ => unreachable!(), @@ -552,6 +570,7 @@ impl Class { /// Find an implementation of an interface method on the target class #[tracing::instrument(skip_all)] + #[allow(non_snake_case)] pub fn map_interface_method(&self, method: &'static Method) -> &'static Method { // https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html#jvms-5.4.6 @@ -562,43 +581,37 @@ impl Class { // (ii) a method that was previously resolved by the instruction. // // The rules to select a method with respect to a class or interface C and a method mR are as follows: + let mR = method; // 1. If mR is marked ACC_PRIVATE, then it is the selected method. - if method.is_private() { - return method; + if mR.is_private() { + return mR; } // 2. Otherwise, the selected method is determined by the following lookup procedure: // If C contains a declaration of an instance method m that can override mR (§5.4.5), then m is the selected method. - for instance_method in self.vtable() { - if instance_method.can_override(&*method) { - // We found an implementation - return instance_method; - } - } - + // // Otherwise, if C has a superclass, a search for a declaration of an instance method that can override mR is performed, // starting with the direct superclass of C and continuing with the direct superclass of that class, and so forth, until a // method is found or no further superclasses exist. If a method is found, it is the selected method. - for parent in self.parent_iter() { - for instance_method in parent.vtable() { - if instance_method.can_override(&*method) { - // We found an implementation - return instance_method; - } + + // (This covers both cases. A class VTable also holds the methods of all super classes) + for instance_method in self.vtable() { + if instance_method.can_override(mR) { + return instance_method; } } // Otherwise, the maximally-specific superinterface methods of C are determined (§5.4.3.3). If exactly one matches mR's name // and descriptor and is not abstract, then it is the selected method. if let Some(superinterface_method) = - self.resolve_method_in_superinterfaces(method.name, method.descriptor, true) + self.resolve_method_in_superinterfaces(mR.name, mR.descriptor, true) { return superinterface_method; } - // No implementation found, return the original method - method + // No implementation found + panic!("No viable methods for method selection"); } } diff --git a/runtime/src/objects/class_instance.rs b/runtime/src/objects/class_instance.rs index 8b98542..d8609ea 100644 --- a/runtime/src/objects/class_instance.rs +++ b/runtime/src/objects/class_instance.rs @@ -86,7 +86,7 @@ impl Instance for ClassInstance { fn get_field_value(&self, field: &Field) -> Operand { assert!(!field.is_static()); - self.get_field_value0(field.idx) + self.get_field_value0(field.index()) } fn get_field_value0(&self, field_idx: usize) -> Operand { @@ -115,7 +115,7 @@ impl Instance for ClassInstance { fn put_field_value(&mut self, field: &Field, value: Operand) { assert!(!field.is_static()); - self.put_field_value0(field.idx, value) + self.put_field_value0(field.index(), value) } fn put_field_value0(&mut self, field_idx: usize, value: Operand) { @@ -128,9 +128,9 @@ impl Instance for ClassInstance { let current = &self.fields[field_idx]; assert!( current.is_compatible_with(&value), - "Expected type compatible with: {:?}, found: {:?}", - current, - value + "Expected type compatible with: {current:?}, found: {value:?} (class: {}, \ + field index: {field_idx})", + self.class.name.as_str(), ); self.fields[field_idx] = value; diff --git a/runtime/src/objects/constant_pool/cp_types.rs b/runtime/src/objects/constant_pool/cp_types.rs index 6c7acb2..800d3a4 100644 --- a/runtime/src/objects/constant_pool/cp_types.rs +++ b/runtime/src/objects/constant_pool/cp_types.rs @@ -225,7 +225,13 @@ impl EntryType for MethodRef { let descriptor_entry = ConstantUtf8::resolve(class, cp, descriptor_index); let descriptor = ConstantUtf8::resolved_entry(descriptor_entry); - let method_ref = ClassObj::resolve_method(class, is_interface, name, descriptor).unwrap(); + let method_ref; + if is_interface { + method_ref = class.resolve_interface_method(name, descriptor).unwrap(); + } else { + method_ref = class.resolve_method(name, descriptor).unwrap(); + } + ResolvedEntry { method_ref } } } diff --git a/runtime/src/objects/field.rs b/runtime/src/objects/field.rs index 20c7d86..42d3db8 100644 --- a/runtime/src/objects/field.rs +++ b/runtime/src/objects/field.rs @@ -2,18 +2,20 @@ use super::reference::Reference; use crate::objects::class::Class; use crate::objects::constant_pool::{cp_types, ConstantPool}; +use std::cell::SyncUnsafeCell; use std::fmt::{Debug, Formatter}; use classfile::accessflags::FieldAccessFlags; use classfile::{FieldInfo, FieldType}; use common::int_types::u2; use instructions::Operand; -use symbols::Symbol; +use symbols::{sym, Symbol}; // TODO: Make more fields private -#[derive(Clone, PartialEq)] pub struct Field { - pub idx: usize, // Used to set the value on `ClassInstance`s + // Used to set the value on `ClassInstance`s + // This is an `UnsafeCell`, as the index will be mutated for injected fields. + idx: SyncUnsafeCell, pub class: &'static Class, pub access_flags: FieldAccessFlags, pub name: Symbol, @@ -29,7 +31,7 @@ impl Debug for Field { "{}#{} (index: {})", self.class.name.as_str(), self.name.as_str(), - self.idx + self.index() ) } } @@ -48,6 +50,16 @@ impl Field { } } +impl Field { + pub fn index(&self) -> usize { + unsafe { *self.idx.get() } + } + + pub(super) unsafe fn set_index(&self, index: usize) { + unsafe { *self.idx.get() = index }; + } +} + impl Field { /// Create a new `Field` instance /// @@ -73,7 +85,7 @@ impl Field { .map(|constant_value| constant_value.constantvalue_index); Box::leak(Box::new(Self { - idx, + idx: SyncUnsafeCell::new(idx), class, access_flags, name, @@ -82,20 +94,35 @@ impl Field { })) } + pub fn new_injected( + class: &'static Class, + name: Symbol, + descriptor: FieldType, + ) -> &'static Field { + Box::leak(Box::new(Self { + idx: SyncUnsafeCell::new(0), + class, + access_flags: FieldAccessFlags::NONE, + name, + descriptor, + constant_value_index: None, + })) + } + pub fn get_static_value(&self) -> Operand { if self.is_volatile() { - self.class.static_field_value_volatile(self.idx) + self.class.static_field_value_volatile(self.index()) } else { - self.class.static_field_value(self.idx) + self.class.static_field_value(self.index()) } } pub fn set_static_value(&self, value: Operand) { if self.is_volatile() { - unsafe { self.class.set_static_field_volatile(self.idx, value) } + unsafe { self.class.set_static_field_volatile(self.index(), value) } } else { unsafe { - self.class.set_static_field(self.idx, value); + self.class.set_static_field(self.index(), value); } } } diff --git a/runtime/src/objects/method/spec.rs b/runtime/src/objects/method/spec.rs index 3c7fdfd..e63a71c 100644 --- a/runtime/src/objects/method/spec.rs +++ b/runtime/src/objects/method/spec.rs @@ -33,29 +33,55 @@ impl Method { } /// Whether this method can override the provided instance method ([§5.4.3.3](https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-5.html#jvms-5.4.5)) + #[allow(non_snake_case)] pub fn can_override(&self, other: &Method) -> bool { // An instance method mC can override another instance method mA iff all of the following are true: + let mC = self; + let mA = other; + // mC has the same name and descriptor as mA. - // + if mC.name != mA.name || mC.descriptor != mA.descriptor { + return false; + } + // mC is not marked ACC_PRIVATE. - // + if mC.is_private() { + return false; + } + // One of the following is true: - // + // mA is marked ACC_PUBLIC. - // // mA is marked ACC_PROTECTED. - // + if mA.is_public() || mA.is_protected() { + return true; + } + // mA is marked neither ACC_PUBLIC nor ACC_PROTECTED nor ACC_PRIVATE, and either: - // - // (a) the declaration of mA appears in the same run-time package as the declaration of mC, or - // (b) if mA is declared in a class A and mC is declared in a class C, then there exists a method mB declared in a class B - // such that C is a subclass of B and B is a subclass of A and mC can override mB and mB can override mA. - - (self.name == other.name && self.descriptor == other.descriptor) - && !self.is_private() - && (other.is_public() - || other.is_protected() - || (!other.is_private() && other.class.shares_package_with(self.class))) + if !mA.is_private() { + // (a) the declaration of mA appears in the same run-time package as the declaration of mC, or + if mA.class.shares_package_with(mC.class) { + return true; + } + + // (b) if mA is declared in a class A and mC is declared in a class C, then there exists a method mB declared in a class B + // such that C is a subclass of B and B is a subclass of A and mC can override mB and mB can override mA. + let mA_class = mA.class; + let mC_class = mC.class; + + for applicable_supers in mC_class + .parent_iter() + .find(|parent| parent.super_class == Some(mA_class)) + { + for mB in applicable_supers.vtable() { + if mC.can_override(mB) && mB.can_override(mA) { + return true; + } + } + } + } + + false } } diff --git a/runtime/src/objects/mirror.rs b/runtime/src/objects/mirror.rs index 3b9b267..0916dcd 100644 --- a/runtime/src/objects/mirror.rs +++ b/runtime/src/objects/mirror.rs @@ -185,7 +185,7 @@ impl Instance for MirrorInstance { } fn get_field_value(&self, field: &Field) -> Operand { - self.get_field_value0(field.idx) + self.get_field_value0(field.index()) } fn get_field_value0(&self, field_idx: usize) -> Operand { @@ -202,7 +202,7 @@ impl Instance for MirrorInstance { } fn put_field_value(&mut self, field: &Field, value: Operand) { - self.put_field_value0(field.idx, value) + self.put_field_value0(field.index(), value) } fn put_field_value0(&mut self, field_idx: usize, value: Operand) { diff --git a/runtime/src/string_interner.rs b/runtime/src/string_interner.rs index edb5856..609954f 100644 --- a/runtime/src/string_interner.rs +++ b/runtime/src/string_interner.rs @@ -109,16 +109,6 @@ impl StringInterner { _ => unreachable!(), } } - - pub fn symbol_from_java_string(class: ClassInstanceRef) -> Symbol { - STRING_POOL - .read() - .unwrap() - .iter() - .find_map(|(symbol, instance)| (instance == &class).then(|| symbol)) - .copied() - .unwrap() - } } fn str_is_latin1(string: &str) -> bool { diff --git a/runtime/src/thread/exceptions.rs b/runtime/src/thread/exceptions.rs index 32831e5..5ce33d1 100644 --- a/runtime/src/thread/exceptions.rs +++ b/runtime/src/thread/exceptions.rs @@ -1,4 +1,5 @@ use super::JavaThread; +use crate::classpath::loader::ClassLoader; use crate::java_call; use crate::objects::class_instance::ClassInstance; use crate::objects::reference::Reference; @@ -22,6 +23,16 @@ impl Throws { pub fn threw(&self) -> bool { matches!(self, Throws::Exception(_)) } + + pub fn expect(self, msg: &str) -> T { + match self { + Throws::Ok(t) => t, + Throws::Exception(e) => panic!( + "{msg}: thread threw {:?} with message: {:?}", + e.kind, e.message + ), + } + } } impl Try for Throws { @@ -107,7 +118,7 @@ impl Exception { } pub fn throw(self, thread: &JavaThread) { - let obj = self.kind.obj(); + let this = self.kind.obj(); match self.message { Some(message) => { @@ -124,6 +135,7 @@ impl Exception { java_call!( thread, init_method, + Operand::Reference(this.clone()), Operand::Reference(Reference::class(string_object)) ); }, @@ -137,11 +149,11 @@ impl Exception { ) .expect("method should exist"); - java_call!(thread, init_method); + java_call!(thread, init_method, Operand::Reference(this.clone())); }, } - handle_throw(thread, obj); + thread.set_pending_exception(this); } } @@ -173,7 +185,7 @@ macro_rules! throw { } macro_rules! handle_exception { - ($thread:ident, $throwsy_expr:expr) => {{ + ($thread:expr, $throwsy_expr:expr) => {{ match $throwsy_expr { crate::thread::exceptions::Throws::Ok(__val) => __val, crate::thread::exceptions::Throws::Exception(__exception) => { @@ -184,11 +196,11 @@ macro_rules! handle_exception { }}; } -use crate::classpath::classloader::ClassLoader; pub(crate) use {handle_exception, throw}; /// See [`JavaThread::throw_exception`] -pub(super) fn handle_throw(thread: &JavaThread, object_ref: Reference) { +#[must_use = "must know whether the exception was thrown"] +pub(super) fn handle_throw(thread: &JavaThread, object_ref: Reference) -> bool { // https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.athrow // The objectref must be of type reference and must refer to an object that is an instance of class Throwable or of a subclass of Throwable. @@ -210,7 +222,7 @@ pub(super) fn handle_throw(thread: &JavaThread, object_ref: Reference) { .method() .find_exception_handler(class_instance.get().class(), current_frame_pc) { - // The pc register is reset to that location,the operand stack of the current frame is cleared, objectref + // The pc register is reset to that location, the operand stack of the current frame is cleared, objectref // is pushed back onto the operand stack, and execution continues. thread.pc.store(handler_pc, Ordering::Relaxed); @@ -218,15 +230,17 @@ pub(super) fn handle_throw(thread: &JavaThread, object_ref: Reference) { stack.clear(); stack.push_reference(object_ref); - return; + return false; } let _ = thread.frame_stack.pop(); } // No handler found, we have to print the stack trace and exit - thread.frame_stack.clear(); + thread.set_exiting(); print_stack_trace(thread, object_ref); + + false } fn print_stack_trace(thread: &JavaThread, object_ref: Reference) { diff --git a/runtime/src/thread/mod.rs b/runtime/src/thread/mod.rs index df3390d..a9db247 100644 --- a/runtime/src/thread/mod.rs +++ b/runtime/src/thread/mod.rs @@ -20,14 +20,14 @@ use crate::thread::frame::native::NativeFrame; use crate::thread::frame::Frame; use std::cell::{Cell, SyncUnsafeCell, UnsafeCell}; -use std::ptr::NonNull; -use std::sync::atomic::{AtomicIsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering}; use std::thread::JoinHandle; use classfile::accessflags::MethodAccessFlags; use instructions::{Operand, StackLike}; use java_lang_Thread::ThreadStatus; use jni::env::JniEnv; +use jni::sys::JNIEnv; use symbols::sym; #[thread_local] @@ -42,7 +42,7 @@ pub struct JVMOptions { #[repr(C)] pub struct JavaThread { - env: JniEnv, + env: JNIEnv, obj: UnsafeCell>, os_thread: UnsafeCell>>, @@ -55,6 +55,9 @@ pub struct JavaThread { frame_stack: FrameStack, remaining_operand: UnsafeCell>>, + + pending_exception: UnsafeCell>, + exiting: AtomicBool, } unsafe impl Sync for JavaThread {} @@ -72,7 +75,7 @@ impl JavaThread { fn new(obj: Option) -> Self { let seed = 1; JavaThread { - env: unsafe { JniEnv::from_raw(new_env()) }, + env: unsafe { new_env() }, obj: UnsafeCell::new(obj), os_thread: UnsafeCell::new(None), @@ -81,6 +84,9 @@ impl JavaThread { pc: AtomicIsize::new(0), frame_stack: FrameStack::new(), remaining_operand: UnsafeCell::new(None), + + pending_exception: UnsafeCell::new(None), + exiting: AtomicBool::new(false), } } @@ -90,7 +96,7 @@ impl JavaThread { /// /// The caller must ensure that `env` is a pointer obtained from [`Self::env()`], and that it is /// valid for the current thread. - pub unsafe fn for_env(env: *const JniEnv) -> *mut JavaThread { + pub unsafe fn for_env(env: *const JNIEnv) -> *mut JavaThread { unsafe { let ptr = env.sub(core::mem::offset_of!(JavaThread, env)); ptr.cast::() as _ @@ -275,10 +281,10 @@ impl JavaThread { } impl JavaThread { - /// Get a pointer to the associated [`JniEnv`] - pub fn env(&self) -> NonNull { + /// Get a pointer to the associated [`JNIEnv`] + pub fn env(&self) -> JniEnv { unsafe { - NonNull::new_unchecked( + JniEnv::from_raw( std::ptr::from_ref(self).add(core::mem::offset_of!(JavaThread, env)) as _, ) } @@ -371,9 +377,15 @@ impl JavaThread { assert!( self.frame_stack.pop_native().is_some(), - "native frame consumed" + "native frame consumed", ); + // Exception from native code, nothing left to do + if self.has_pending_exception() { + self.throw_pending_exception(false); + return; + } + // Push the return value onto the previous frame's stack if let Some(ret) = ret { self.frame_stack.current().unwrap().stack_mut().push_op(ret); @@ -431,6 +443,27 @@ impl JavaThread { Interpreter::instruction(current_frame); } } +} + +// Exceptions +impl JavaThread { + pub fn set_pending_exception(&self, exception: Reference) { + unsafe { *self.pending_exception.get() = Some(exception) } + } + + pub fn has_pending_exception(&self) -> bool { + unsafe { (*self.pending_exception.get()).is_some() } + } + + fn set_exiting(&self) { + self.exiting.store(true, Ordering::Relaxed); + self.frame_stack.clear(); + self.pc.store(0, Ordering::Relaxed); + } + + pub fn is_exiting(&self) -> bool { + self.exiting.load(Ordering::Relaxed) + } /// Throw an exception on this thread /// @@ -441,6 +474,26 @@ impl JavaThread { /// This will panic if `object_ref` is non-null, but not a subclass of `java/lang/Throwable`. /// This should never occur post-verification. pub fn throw_exception(&self, object_ref: Reference) { - exceptions::handle_throw(self, object_ref); + // `throw_exception` is only ever called while the thread is guaranteed to be running, so + // we don't care whether the exception was handled or not here. + let _ = exceptions::handle_throw(self, object_ref); + } + + /// Throw the pending exception on this thread + pub fn throw_pending_exception(&self, during_vm_init: bool) { + let pending_exception = unsafe { (*self.pending_exception.get()).take() }; + let Some(exception) = pending_exception else { + return; + }; + + if !exceptions::handle_throw(self, exception) { + // The exception was handled, nothing to do. + return; + } + + // An exception thrown during VM init will stop the thread, need to start it back up to print the stack trace. + if during_vm_init { + self.run(); + } } } diff --git a/symbols/src/lib.rs b/symbols/src/lib.rs index d852353..8d86daf 100644 --- a/symbols/src/lib.rs +++ b/symbols/src/lib.rs @@ -138,6 +138,8 @@ macro_rules! sym { // NOTE: **ONLY ADD MANUAL ENTRIES ABOVE THE MARKER COMMENTS** // Other generators may inject into this macro, take note of the marker comments below vm_symbols::define_symbols! { + EMPTY: "", + // Classes // Primitive @@ -264,4 +266,10 @@ vm_symbols::define_symbols! { methodName: "methodName", fileName: "fileName", lineNumber: "lineNumber", + unnamedModule: "unnamedModule", + nameAndId: "nameAndId", + + // Injected fields + loader_ptr, + module_ptr, } diff --git a/tools/sj/src/lib.rs b/tools/sj/src/lib.rs index 245b5dd..ce71511 100644 --- a/tools/sj/src/lib.rs +++ b/tools/sj/src/lib.rs @@ -7,6 +7,7 @@ use crate::error::{Error, Result}; use std::path::Path; use clap::Parser; +use jni::error::JniError; use jvm_runtime::classpath::{add_classpath_entry, jar, jimage, ClassPathEntry}; #[tracing::instrument] @@ -41,7 +42,11 @@ pub fn launch() -> Result<()> { return Ok(()); } - native::invoke_main_method(env, main_class, args.args)?; + match native::invoke_main_method(env, main_class, args.args) { + Err(Error::Jni(JniError::ExceptionThrown)) => env.exception_describe(), + Err(e) => return Err(e), + _ => {}, + } Ok(()) }