From f705ea7eb766ff1a2228c876f29247c9811a8a6a Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 28 Dec 2024 20:00:33 -0500 Subject: [PATCH] wip --- platform/src/family/mod.rs | 2 + platform/src/family/properties.rs | 115 +++++++++ platform/src/family/unix/linux/locale.rs | 106 ++++++++ platform/src/family/unix/linux/mod.rs | 1 + platform/src/family/unix/linux/properties.rs | 228 +++++++++++++++++- platform/src/family/unix/locale.rs | 114 +++++++++ platform/src/family/unix/macos/properties.rs | 10 +- platform/src/family/unix/mod.rs | 1 + platform/src/family/windows/properties.rs | 4 + runtime/src/globals/classes.rs | 1 + runtime/src/globals/field_offsets.rs | 17 ++ runtime/src/initialization.rs | 20 ++ runtime/src/native/java/io/FileSystem.rs | 3 + runtime/src/native/java/io/UnixFileSystem.rs | 73 +++++- runtime/src/native/java/io/def/FileSystem.def | 15 ++ runtime/src/native/java/lang/ref/Reference.rs | 11 +- .../native/jdk/internal/util/SystemProps.rs | 89 +++---- runtime/src/native/mod.rs | 30 +-- runtime/src/objects/instance.rs | 2 +- symbols/src/lib.rs | 1 + 20 files changed, 773 insertions(+), 70 deletions(-) create mode 100644 platform/src/family/unix/linux/locale.rs create mode 100644 platform/src/family/unix/locale.rs create mode 100644 runtime/src/native/java/io/FileSystem.rs create mode 100644 runtime/src/native/java/io/def/FileSystem.def diff --git a/platform/src/family/mod.rs b/platform/src/family/mod.rs index 5760e2a..b383903 100644 --- a/platform/src/family/mod.rs +++ b/platform/src/family/mod.rs @@ -1,6 +1,8 @@ mod signals; pub use signals::*; +pub mod properties; + use crate::macros::match_cfg_meta; // `target_family` specific exports diff --git a/platform/src/family/properties.rs b/platform/src/family/properties.rs index db32a75..088c411 100644 --- a/platform/src/family/properties.rs +++ b/platform/src/family/properties.rs @@ -4,8 +4,123 @@ pub const CPU_ENDIAN: &str = if cfg!(target_endian = "big") { "big" } else { "little" }; +// TODO: Document +// TODO: Probably make all fields `Option`, to avoid setting empty values at runtime +pub struct PropertySet { + pub display_country: String, + pub display_language: String, + pub display_script: String, + pub display_variant: String, + pub file_encoding: String, + pub file_separator: String, + pub format_country: String, + pub format_language: String, + pub format_script: String, + pub format_variant: String, + pub ftp_nonProxyHosts: Option, + pub ftp_proxyHost: Option, + pub ftp_proxyPort: Option, + pub http_nonProxyHosts: Option, + pub http_proxyHost: Option, + pub http_proxyPort: Option, + pub https_proxyHost: Option, + pub https_proxyPort: Option, + pub java_io_tmpdir: String, + pub line_separator: String, + pub os_arch: String, + pub os_name: String, + pub os_version: String, + pub path_separator: String, + pub socksNonProxyHosts: Option, + pub socksProxyHost: Option, + pub socksProxyPort: Option, + pub stderr_encoding: Option, + pub stdout_encoding: Option, + pub sun_arch_abi: Option, + pub sun_arch_data_model: String, + pub sun_cpu_endian: String, + pub sun_cpu_isalist: Option, + pub sun_io_unicode_encoding: String, + pub sun_jnu_encoding: String, + pub sun_os_patch_level: String, + pub user_dir: String, + pub user_home: String, + pub user_name: String, +} + +impl PropertySet { + pub fn fill(&mut self) -> Result<(), Error> { + fill_properties_impl(self) + } +} + +impl Default for PropertySet { + fn default() -> Self { + PropertySet { + // Set the constant properties, exported below + file_separator: FILE_SEPARATOR.into(), + line_separator: LINE_SEPARATOR.into(), + os_arch: OS_ARCH.into(), + path_separator: PATH_SEPARATOR.into(), + sun_cpu_endian: CPU_ENDIAN.into(), + sun_io_unicode_encoding: UNICODE_ENCODING.into(), + + // Others will need to be filled by their platform-specific implementations + display_country: String::new(), + display_language: String::new(), + display_script: String::new(), + display_variant: String::new(), + file_encoding: String::new(), + format_country: String::new(), + format_language: String::new(), + format_script: String::new(), + format_variant: String::new(), + ftp_nonProxyHosts: None, + ftp_proxyHost: None, + ftp_proxyPort: None, + http_nonProxyHosts: None, + http_proxyHost: None, + http_proxyPort: None, + https_proxyHost: None, + https_proxyPort: None, + java_io_tmpdir: String::new(), + os_name: String::new(), + os_version: String::new(), + socksNonProxyHosts: None, + socksProxyHost: None, + socksProxyPort: None, + stderr_encoding: None, + stdout_encoding: None, + sun_arch_abi: None, + sun_arch_data_model: String::new(), + sun_cpu_isalist: None, + sun_jnu_encoding: String::new(), + sun_os_patch_level: String::new(), + user_dir: String::new(), + user_home: String::new(), + user_name: String::new(), + } + } +} + +#[derive(Debug)] +pub enum Error { + WorkingDir, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Error::WorkingDir => f.write_str("Could not determine currenting working directory") + } + } +} + +impl core::error::Error for Error {} + // Export family specific properties +use std::fmt::{Display, Formatter}; #[cfg(target_family = "unix")] pub use super::unix::properties::*; #[cfg(target_family = "windows")] diff --git a/platform/src/family/unix/linux/locale.rs b/platform/src/family/unix/linux/locale.rs new file mode 100644 index 0000000..edd6568 --- /dev/null +++ b/platform/src/family/unix/linux/locale.rs @@ -0,0 +1,106 @@ +use core::ffi::CStr; + +pub(super) fn locale_aliases() -> impl Iterator { + const LINUX_LOCALE_ALIASES: &[(&CStr, &CStr)] = &[ + (c"hs", c"en_US"), // used on Linux, not clear what it stands for + (c"ua", c"en_US"), // used on Linux, not clear what it stands for + (c"bokmal", c"nb_NO"), + (c"bokm\xE5cl", c"nb_NO"), + (c"catalan", c"ca_ES"), + (c"croatian", c"hr_HR"), + (c"czech", c"cs_CZ"), + (c"danish", c"da_DK"), + (c"dansk", c"da_DK"), + (c"deutsch", c"de_DE"), + (c"dutch", c"nl_NL"), + (c"eesti", c"et_EE"), + (c"estonian", c"et_EE"), + (c"finnish", c"fi_FI"), + (c"fran\xE7c\x61is", c"fr_FR"), + (c"french", c"fr_FR"), + (c"galego", c"gl_ES"), + (c"galician", c"gl_ES"), + (c"german", c"de_DE"), + (c"greek", c"el_GR"), + (c"hebrew", c"iw_IL"), + (c"hrvatski", c"hr_HR"), + (c"hungarian", c"hu_HU"), + (c"icelandic", c"is_IS"), + (c"italian", c"it_IT"), + (c"japanese", c"ja_JP"), + (c"korean", c"ko_KR"), + (c"lithuanian", c"lt_LT"), + (c"norwegian", c"no_NO"), + (c"nynorsk", c"nn_NO"), + (c"polish", c"pl_PL"), + (c"portuguese", c"pt_PT"), + (c"romanian", c"ro_RO"), + (c"russian", c"ru_RU"), + (c"slovak", c"sk_SK"), + (c"slovene", c"sl_SI"), + (c"slovenian", c"sl_SI"), + (c"spanish", c"es_ES"), + (c"swedish", c"sv_SE"), + (c"thai", c"th_TH"), + (c"turkish", c"tr_TR"), + ]; + + crate::locale::base_locale_aliases().chain(LINUX_LOCALE_ALIASES.into_iter()) +} + +pub(super) fn language_names() -> impl Iterator { + const LINUX_LANGUAGE_NAMES: &[(&CStr, &CStr)] = &[ + (c"hs", c"en"), // used on Linux, not clear what it stands for + (c"ua", c"en"), // used on Linux, not clear what it stands for + (c"catalan", c"ca"), + (c"croatian", c"hr"), + (c"czech", c"cs"), + (c"danish", c"da"), + (c"dansk", c"da"), + (c"deutsch", c"de"), + (c"dutch", c"nl"), + (c"finnish", c"fi"), + (c"fran\xE7c\x61is", c"fr"), + (c"french", c"fr"), + (c"german", c"de"), + (c"greek", c"el"), + (c"hebrew", c"he"), + (c"hrvatski", c"hr"), + (c"hungarian", c"hu"), + (c"icelandic", c"is"), + (c"italian", c"it"), + (c"japanese", c"ja"), + (c"norwegian", c"no"), + (c"polish", c"pl"), + (c"portuguese", c"pt"), + (c"romanian", c"ro"), + (c"russian", c"ru"), + (c"slovak", c"sk"), + (c"slovene", c"sl"), + (c"slovenian", c"sl"), + (c"spanish", c"es"), + (c"swedish", c"sv"), + (c"turkish", c"tr"), + ]; + + crate::locale::base_language_names().chain(LINUX_LANGUAGE_NAMES.into_iter()) +} + +pub(super) fn script_names() -> impl Iterator { + const LINUX_SCRIPT_NAMES: &[(&CStr, &CStr)] = &[ + (c"cyrillic", c"Cyrl"), + (c"devanagari", c"Deva"), + (c"iqtelif", c"Latn"), + (c"latin", c"Latn"), + ]; + + crate::locale::base_script_names().chain(LINUX_SCRIPT_NAMES.into_iter()) +} + +pub(super) fn country_names() -> impl Iterator { + const LINUX_COUNTRY_NAMES: &[(&CStr, &CStr)] = &[ + (c"RN", c"US"), // used on Linux, not clear what it stands for + ]; + + crate::locale::base_country_names().chain(LINUX_COUNTRY_NAMES.into_iter()) +} diff --git a/platform/src/family/unix/linux/mod.rs b/platform/src/family/unix/linux/mod.rs index 09d2bb6..30d14c8 100644 --- a/platform/src/family/unix/linux/mod.rs +++ b/platform/src/family/unix/linux/mod.rs @@ -1 +1,2 @@ +mod locale; pub mod properties; diff --git a/platform/src/family/unix/linux/properties.rs b/platform/src/family/unix/linux/properties.rs index e369d33..8323a5c 100644 --- a/platform/src/family/unix/linux/properties.rs +++ b/platform/src/family/unix/linux/properties.rs @@ -1,6 +1,11 @@ #![cfg_attr(rustfmt, rustfmt_skip)] -// TODO +use crate::properties::{Error, PropertySet}; + +use core::ffi::CStr; +use std::io::BufRead; +use core::mem::MaybeUninit; +use core::ptr::{self, NonNull}; // @Native private static final int _display_country_NDX = 0; // @Native private static final int _display_language_NDX = 1 + _display_country_NDX; @@ -12,6 +17,7 @@ // @Native private static final int _format_script_NDX = 1 + _format_language_NDX; // @Native private static final int _format_variant_NDX = 1 + _format_script_NDX; // @Native private static final int _java_io_tmpdir_NDX = 1 + _https_proxyPort_NDX; +const JAVA_IO_TMPDIR: &str = "/var/tmp"; // @Native private static final int _os_name_NDX = 1 + _os_arch_NDX; // @Native private static final int _os_version_NDX = 1 + _os_name_NDX; // @Native private static final int _stderr_encoding_NDX = 1 + _socksProxyPort_NDX; @@ -24,3 +30,223 @@ // @Native private static final int _user_dir_NDX = 1 + _sun_os_patch_level_NDX; // @Native private static final int _user_home_NDX = 1 + _user_dir_NDX; // @Native private static final int _user_name_NDX = 1 + _user_home_NDX; + +pub fn fill_properties_impl(props: &mut PropertySet) -> Result<(), Error> { + props.java_io_tmpdir = JAVA_IO_TMPDIR.into(); + + // OS props + { + let mut utsname = unsafe { MaybeUninit::::zeroed().assume_init() }; + unsafe { + libc::uname(&raw mut utsname); + } + + let sysname_raw = unsafe { + CStr::from_ptr(utsname.sysname.as_ptr()) + }; + props.os_name = sysname_raw.to_str().expect("TODO: Other string encodings").to_owned(); + + let release_raw = unsafe { + CStr::from_ptr(utsname.release.as_ptr()) + }; + props.os_version = release_raw.to_str().expect("TODO: Other string encodings").to_owned(); + } + + // Locale props + { + unsafe { libc::setlocale(libc::LC_ALL, c"".as_ptr()) }; + init_locale( + libc::LC_CTYPE, + Some(&mut props.format_language), + Some(&mut props.format_script), + Some(&mut props.format_country), + Some(&mut props.format_variant), + Some(&mut props.file_encoding) + ); + init_locale( + libc::LC_MESSAGES, + Some(&mut props.display_language), + Some(&mut props.display_script), + Some(&mut props.display_country), + Some(&mut props.display_variant), + None + ); + + props.sun_jnu_encoding = props.file_encoding.clone(); + } + + // User props + { + let uid = unsafe { libc::getuid() }; + let passwd = unsafe { libc::getpwuid(uid) }; + + let user_name; + let user_home; + if passwd.is_null() { + user_name = String::from("?"); + user_home = None; + } else { + let user_name_raw = unsafe { + CStr::from_ptr((*passwd).pw_name) + }; + user_name = user_name_raw.to_str().expect("TODO: Other string encodings").to_owned(); + + let user_home_raw = unsafe { + CStr::from_ptr((*passwd).pw_dir) + }; + + if user_home_raw.to_bytes().len() < 3 { + user_home = None; + } else { + user_home = Some(user_home_raw.to_str().expect("TODO: Other string encodings").to_owned()); + } + } + + let user_home = match user_home { + Some(user_home) => user_home, + None => { + match std::env::var("HOME") { + Ok(env_home) if env_home.len() > 2 => { + env_home + } + _ => String::from("?") + } + } + }; + } + + // Current directory + { + let Ok(dir) = std::env::current_dir() else { + return Err(Error::WorkingDir) + }; + + props.user_dir = dir.to_str().expect("TODO: Other path encodings").to_owned(); + } + + Ok(()) +} + +fn init_locale(category: i32, language_field: Option<&mut String>, script_field: Option<&mut String>, country_field: Option<&mut String>, variant_field: Option<&mut String>, encoding_field: Option<&mut String>) { + // Parses the locale in the form: `language_country.encoding@variant` + // + // `language_country`: Required + // `encoding`: Optional, preceded by . + // `variant`: Optional, preceded by @ + fn split_locale(locale: &CStr) -> (&[u8], &[u8], Option<&[u8]>, Option<&[u8]>) { + let bytes = locale.to_bytes(); + + let mut has_encoding_or_variant = false; + let mut language_country_end_pos = bytes.len(); + + // Encoding + let mut encoding = None; + let variant_position = bytes.iter().position(|b| *b == b'@'); + if let Some(pos) = bytes.iter().position(|b| *b == b'.') { + language_country_end_pos = pos; + + has_encoding_or_variant = true; + let end = variant_position.unwrap_or(bytes.len()); + encoding = Some(&bytes[pos..end]); + } + + // Variant + let mut variant = None; + if let Some(pos) = variant_position { + if encoding.is_none() { + language_country_end_pos = pos; + } + + has_encoding_or_variant = true; + variant = Some(&bytes[pos..]); + } + + let mut language_country = bytes[..language_country_end_pos].split(|b| *b == b'_'); + + let language = language_country.next().expect("TODO: Nice error"); + let country = language_country.next().expect("TODO: Nice error"); + + (language, country, encoding, variant) + } + + let locale_raw = unsafe { libc::setlocale(category, ptr::null()) }; + + let locale; + if locale_raw.is_null() { + locale = c"en_US"; + } else { + locale = unsafe { CStr::from_ptr(locale_raw) }; + } + + let (mut language, mut country, mut encoding, mut variant) = split_locale(locale); + + if encoding.is_none() && variant.is_none() { + if let Some((_, new_locale)) = super::locale::locale_aliases().find(|(k, _)| *k == locale) { + (language, country, encoding, variant) = split_locale(new_locale); + } + } + + // Normalize the language name + if let Some(language_field) = language_field { + // In case we can't find anything... + *language_field = String::from("en"); + + if let Some((_, language)) = super::locale::language_names().find(|(k, _)| k.to_bytes() == language) { + let language = language.to_str().expect("normalized table entries should never be invalid"); + *language_field = String::from(language); + } + } + + // Normalize the country name + if let Some(country_field) = country_field { + if let Some((_, country)) = super::locale::country_names().find(|(k, _)| k.to_bytes() == country) { + let country = country.to_str().expect("normalized table entries should never be invalid"); + *country_field = String::from(country); + } + } + + // Normalize the script and variant name. + // Note that we only use variants listed in the mapping array; others are ignored. + if let Some(variant) = variant { + if let Some(script_field) = script_field { + if let Some((_, script)) = super::locale::script_names().find(|(k, _)| k.to_bytes() == variant) { + let script = script.to_str().expect("normalized table entries should never be invalid"); + *script_field = String::from(script); + } + } + + if let Some(variant_field) = variant_field { + if let Some((_, variant)) = crate::locale::base_variant_names().find(|(k, _)| k.to_bytes() == variant) { + let variant = variant.to_str().expect("normalized table entries should never be invalid"); + *variant_field = String::from(variant); + } + } + } + + // Normalize the encoding name. Note that we IGNORE the string 'encoding' extracted from the + // locale name above. Instead, we use the more reliable method of calling `nl_langinfo(CODESET)`. + // This function returns an empty string if no encoding is set for the given + // locale (e.g., the C or POSIX locales); we use the default ISO 8859-1 converter for such locales. + if let Some(encoding) = encoding { + if let Some(encoding_field) = encoding_field { + let ret_encoding; + match encoding { + b"ISO8859-15" => ret_encoding = "ISO8859-15", + // Remap the encoding string to a different value for japanese locales on linux so that + // customized converters are used instead of the default converter for "EUC-JP" + b"EUC-JP" => ret_encoding = "EUC-JP-LINUX", + _ => { + let lang = unsafe { libc::nl_langinfo(libc::CODESET) }; + if lang.is_null() { + ret_encoding = "ISO8859-1"; + } else { + let cstr = unsafe { CStr::from_ptr(lang) }; + ret_encoding = cstr.to_str().expect("TODO: Nice error"); + } + } + } + + *encoding_field = String::from(ret_encoding); + } + } +} diff --git a/platform/src/family/unix/locale.rs b/platform/src/family/unix/locale.rs new file mode 100644 index 0000000..d51a60c --- /dev/null +++ b/platform/src/family/unix/locale.rs @@ -0,0 +1,114 @@ +// Adapted from https://github.com/openjdk/jdk/blob/master/src/java.base/unix/native/libjava/locale_str.h +// +// No conditional compilation here, these iterators are extended in the os-specific dirs. +// For example, see: ./linux/locale.rs + +use core::ffi::CStr; + +pub(crate) fn base_locale_aliases() -> impl Iterator +{ + const LOCALE_ALIASES: &'static [(&CStr, &CStr)] = &[ + (c"ar", c"ar_EG"), + (c"be", c"be_BY"), + (c"bg", c"bg_BG"), + (c"br", c"br_FR"), + (c"ca", c"ca_ES"), + (c"cs", c"cs_CZ"), + (c"cz", c"cs_CZ"), + (c"da", c"da_DK"), + (c"de", c"de_DE"), + (c"el", c"el_GR"), + (c"en", c"en_US"), + (c"eo", c"eo"), // no country for Esperanto + (c"es", c"es_ES"), + (c"et", c"et_EE"), + (c"eu", c"eu_ES"), + (c"fi", c"fi_FI"), + (c"fr", c"fr_FR"), + (c"ga", c"ga_IE"), + (c"gl", c"gl_ES"), + (c"he", c"iw_IL"), + (c"hr", c"hr_HR"), + (c"hu", c"hu_HU"), + (c"id", c"in_ID"), + (c"in", c"in_ID"), + (c"is", c"is_IS"), + (c"it", c"it_IT"), + (c"iw", c"iw_IL"), + (c"ja", c"ja_JP"), + (c"kl", c"kl_GL"), + (c"ko", c"ko_KR"), + (c"lt", c"lt_LT"), + (c"lv", c"lv_LV"), + (c"mk", c"mk_MK"), + (c"nl", c"nl_NL"), + (c"no", c"no_NO"), + (c"pl", c"pl_PL"), + (c"pt", c"pt_PT"), + (c"ro", c"ro_RO"), + (c"ru", c"ru_RU"), + (c"se", c"se_NO"), + (c"sk", c"sk_SK"), + (c"sl", c"sl_SI"), + (c"sq", c"sq_AL"), + (c"sr", c"sr_CS"), + (c"su", c"fi_FI"), + (c"sv", c"sv_SE"), + (c"th", c"th_TH"), + (c"tr", c"tr_TR"), + (c"uk", c"uk_UA"), + (c"vi", c"vi_VN"), + (c"wa", c"wa_BE"), + (c"zh", c"zh_CN"), + ]; + + LOCALE_ALIASES.into_iter() +} + +pub(crate) fn base_language_names() -> impl Iterator +{ + const LANGUAGE_NAMES: &[(&'static CStr, &'static CStr)] = &[ + (c"C", c"en"), + (c"POSIX", c"en"), + (c"cz", c"cs"), + (c"he", c"iw"), + (c"id", c"in"), + (c"sh", c"sr"), // sh is deprecated + (c"su", c"fi"), + ]; + + LANGUAGE_NAMES.into_iter() +} + +pub(crate) fn base_script_names() -> impl Iterator { + const SCRIPT_NAMES: &[(&CStr, &CStr)] = &[ + (c"Arab", c"Arab"), + (c"Cyrl", c"Cyrl"), + (c"Deva", c"Deva"), + (c"Ethi", c"Ethi"), + (c"Hans", c"Hans"), + (c"Hant", c"Hant"), + (c"Latn", c"Latn"), + (c"Sund", c"Sund"), + (c"Syrc", c"Syrc"), + (c"Tfng", c"Tfng"), + ]; + + SCRIPT_NAMES.into_iter() +} + +pub(crate) fn base_country_names() -> impl Iterator +{ + const COUNTRY_NAMES: &[(&CStr, &CStr)] = &[ + (c"YU", c"CS"), // YU has been removed from ISO 3166 + ]; + + COUNTRY_NAMES.into_iter() +} + +pub(crate) fn base_variant_names() -> impl Iterator +{ + const VARIANT_NAMES: &[(&CStr, &CStr)] = &[(c"nynorsk", c"NY")]; + + VARIANT_NAMES.into_iter() +} diff --git a/platform/src/family/unix/macos/properties.rs b/platform/src/family/unix/macos/properties.rs index 376f35d..11b8ba6 100644 --- a/platform/src/family/unix/macos/properties.rs +++ b/platform/src/family/unix/macos/properties.rs @@ -2,6 +2,8 @@ // TODO +use std::ptr::NonNull; + // @Native private static final int _display_country_NDX = 0; // @Native private static final int _display_language_NDX = 1 + _display_country_NDX; // @Native private static final int _display_script_NDX = 1 + _display_language_NDX; @@ -30,8 +32,14 @@ pub const OS_NAME: &str = "Mac OS X"; // @Native private static final int _sun_arch_abi_NDX = 1 + _stdout_encoding_NDX; // @Native private static final int _sun_arch_data_model_NDX = 1 + _sun_arch_abi_NDX; // @Native private static final int _sun_cpu_isalist_NDX = 1 + _sun_cpu_endian_NDX; -// @Native private static final int _sun_jnu_encoding_NDX = 1 + _sun_io_unicode_encoding_NDX; +pub const SUN_JNU_ENCODING: &str = "UTF-8"; // @Native private static final int _sun_os_patch_level_NDX = 1 + _sun_jnu_encoding_NDX; // @Native private static final int _user_dir_NDX = 1 + _sun_os_patch_level_NDX; // @Native private static final int _user_home_NDX = 1 + _user_dir_NDX; // @Native private static final int _user_name_NDX = 1 + _user_home_NDX; + +pub fn fill_properties_impl(props: &mut crate::properties::PropertySet) -> Result<(), crate::properties::Error> { + props.os_name = OS_NAME; + props.sun_jnu_encoding = SUN_JNU_ENCODING; + unimplemented!("macOS property set"); +} diff --git a/platform/src/family/unix/mod.rs b/platform/src/family/unix/mod.rs index 0760fa9..ba48b3c 100644 --- a/platform/src/family/unix/mod.rs +++ b/platform/src/family/unix/mod.rs @@ -19,5 +19,6 @@ match_cfg_meta! { // Exports pub mod io; +pub(crate) mod locale; pub mod properties; pub(super) mod signals; diff --git a/platform/src/family/windows/properties.rs b/platform/src/family/windows/properties.rs index 4958873..3e76abe 100644 --- a/platform/src/family/windows/properties.rs +++ b/platform/src/family/windows/properties.rs @@ -1,5 +1,6 @@ #![cfg_attr(rustfmt, rustfmt_skip)] +use std::ptr::NonNull; use crate::macros::match_cfg_meta; // TODO @@ -48,3 +49,6 @@ pub const UNICODE_ENCODING: &str = "UnicodeLittle"; // @Native private static final int _user_home_NDX = 1 + _user_dir_NDX; // @Native private static final int _user_name_NDX = 1 + _user_home_NDX; +pub fn fill_properties_impl(props: &mut crate::properties::PropertySet) -> Result<(), crate::properties::Error> { + unimplemented!("Windows property set"); +} diff --git a/runtime/src/globals/classes.rs b/runtime/src/globals/classes.rs index 528309b..9814812 100644 --- a/runtime/src/globals/classes.rs +++ b/runtime/src/globals/classes.rs @@ -47,6 +47,7 @@ define_classes!( java_lang_Throwable, java_lang_Cloneable, java_lang_Module, + java_lang_ref_Reference, java_lang_ref_Finalizer, java_io_FileDescriptor, java_io_FileInputStream, diff --git a/runtime/src/globals/field_offsets.rs b/runtime/src/globals/field_offsets.rs index eddbbbf..3e9c257 100644 --- a/runtime/src/globals/field_offsets.rs +++ b/runtime/src/globals/field_offsets.rs @@ -72,6 +72,23 @@ pub mod java_lang_Module { } } +pub mod java_lang_ref_Reference { + static mut REFERENT_FIELD_OFFSET: usize = 0; + + /// `java.lang.ref.Reference#referent` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected type: `Reference` + pub fn referent_field_offset() -> usize { + unsafe { REFERENT_FIELD_OFFSET } + } + + pub unsafe fn set_referent_field_offset(value: usize) { + REFERENT_FIELD_OFFSET = value; + } +} + pub mod java_lang_Thread { static mut THREAD_EETOP_FIELD_OFFSET: usize = 0; static mut THREAD_HOLDER_FIELD_OFFSET: usize = 0; diff --git a/runtime/src/initialization.rs b/runtime/src/initialization.rs index 69e2890..3a13c1e 100644 --- a/runtime/src/initialization.rs +++ b/runtime/src/initialization.rs @@ -81,6 +81,7 @@ fn load_global_classes() { java_lang_ThreadGroup, java_lang_Throwable, java_lang_Cloneable, + java_lang_ref_Reference, java_lang_ref_Finalizer, jdk_internal_reflect_MethodAccessorImpl, ); @@ -191,6 +192,25 @@ fn init_field_offsets() { } } + // java.lang.ref.Reference + { + let reference_class = crate::globals::classes::java_lang_ref_Reference(); + let reference_referent_field = reference_class + .fields() + .find(|field| { + !field.is_static() + && field.name == sym!(referent) + && matches!(field.descriptor, FieldType::Object(_)) + }) + .expect("java.lang.ref.Reference should have a referent field"); + + unsafe { + crate::globals::field_offsets::java_lang_ref_Reference::set_referent_field_offset( + reference_referent_field.idx, + ); + } + } + // java.lang.Thread java_lang_Thread::set_field_offsets(); } diff --git a/runtime/src/native/java/io/FileSystem.rs b/runtime/src/native/java/io/FileSystem.rs new file mode 100644 index 0000000..b0ff95d --- /dev/null +++ b/runtime/src/native/java/io/FileSystem.rs @@ -0,0 +1,3 @@ +// This class only has native constants + +include_generated!("native/java/io/def/FileSystem.constants.rs"); diff --git a/runtime/src/native/java/io/UnixFileSystem.rs b/runtime/src/native/java/io/UnixFileSystem.rs index 5bd853c..e3f5d2a 100644 --- a/runtime/src/native/java/io/UnixFileSystem.rs +++ b/runtime/src/native/java/io/UnixFileSystem.rs @@ -1,38 +1,95 @@ #![allow(non_upper_case_globals)] use crate::classpath::classloader::ClassLoader; -use crate::native::jni::IntoJni; +use crate::native::jni::{field_ref_from_jfieldid, IntoJni}; +use crate::objects::instance::Instance; use crate::objects::reference::Reference; +use crate::string_interner::StringInterner; use std::cell::SyncUnsafeCell; +use std::mem::MaybeUninit; use std::ptr::{self, NonNull}; use std::sync::atomic::{AtomicBool, Ordering}; use ::jni::env::JniEnv; use ::jni::sys::{jboolean, jfieldID, jint, jlong}; use common::sync::ForceSync; +use common::traits::PtrType; use symbols::sym; include_generated!("native/java/io/def/UnixFileSystem.definitions.rs"); /// `java.io.File#path` field offset -static path: SyncUnsafeCell> = +static java_io_file_path_field: SyncUnsafeCell> = SyncUnsafeCell::new(ForceSync::new(ptr::null_mut() as _)); +fn get_file_path(file: Reference) -> String { + let field_raw = unsafe { &*java_io_file_path_field.get() }; + let field = + unsafe { field_ref_from_jfieldid(field_raw.0) }.expect("field should always be present"); + + let f = file.extract_class(); + let value = f.get().get_field_value(field).expect_reference(); + + StringInterner::rust_string_from_java_string(value.extract_class()) +} pub fn canonicalize0( _: NonNull, _this: Reference, - _path: Reference, // java.lang.String -) -> Reference { - unimplemented!("java.io.UnixFileSystem#canonicalize0"); + path: Reference, // java.lang.String +) -> Reference /* java.lang.String */ { + if path.is_null() { + panic!("NullPointerException"); // TODO + } + + let path_str = StringInterner::rust_string_from_java_string(path.extract_class()); + + let Ok(path) = std::path::Path::new(&path_str).canonicalize() else { + panic!("IOException"); // TODO + }; + + Reference::class(StringInterner::intern_string( + path.to_string_lossy().into_owned(), + )) +} + +#[cfg(unix)] +pub fn getBooleanAttributes0( + _: NonNull, + _this: Reference, + f: Reference, // java.io.File +) -> jint { + use super::FileSystem::{BA_DIRECTORY, BA_EXISTS, BA_REGULAR}; + + use std::os::unix::fs::MetadataExt; + let path = get_file_path(f); + + let Ok(metadata) = std::fs::metadata(path) else { + return 0; + }; + + let mode = metadata.mode(); + let fmt = mode & libc::S_IFMT; + + let mut ret = BA_EXISTS; + if fmt == libc::S_IFREG { + ret |= BA_REGULAR; + } + + if fmt == libc::S_IFDIR { + ret |= BA_DIRECTORY; + } + + ret } +#[cfg(not(unix))] pub fn getBooleanAttributes0( _: NonNull, _this: Reference, _f: Reference, // java.io.File ) -> jint { - unimplemented!("java.io.UnixFileSystem#getBooleanAttributes0"); + 0 } pub fn checkAccess0( @@ -91,7 +148,7 @@ pub fn list0( _: NonNull, _this: Reference, _f: Reference, // java.io.File -) -> Reference { +) -> Reference /* java.lang.String[] */ { unimplemented!("java.io.UnixFileSystem#list0"); } @@ -165,7 +222,7 @@ pub fn initIDs(_: NonNull) { for field in class.fields() { if field.name == sym!(path) { unsafe { - *path.get() = ForceSync::new(field.into_jni()); + *java_io_file_path_field.get() = ForceSync::new(field.into_jni()); } field_set = true; break; diff --git a/runtime/src/native/java/io/def/FileSystem.def b/runtime/src/native/java/io/def/FileSystem.def new file mode 100644 index 0000000..390eb14 --- /dev/null +++ b/runtime/src/native/java/io/def/FileSystem.def @@ -0,0 +1,15 @@ +public class FileSystem { + // Constants for simple boolean attributes + @Native public static final int BA_EXISTS = 0x01; + @Native public static final int BA_REGULAR = 0x02; + @Native public static final int BA_DIRECTORY = 0x04; + @Native public static final int BA_HIDDEN = 0x08; + + @Native public static final int ACCESS_READ = 0x04; + @Native public static final int ACCESS_WRITE = 0x02; + @Native public static final int ACCESS_EXECUTE = 0x01; + + @Native public static final int SPACE_TOTAL = 0; + @Native public static final int SPACE_FREE = 1; + @Native public static final int SPACE_USABLE = 2; +} \ No newline at end of file diff --git a/runtime/src/native/java/lang/ref/Reference.rs b/runtime/src/native/java/lang/ref/Reference.rs index 6d97e13..51b772d 100644 --- a/runtime/src/native/java/lang/ref/Reference.rs +++ b/runtime/src/native/java/lang/ref/Reference.rs @@ -1,3 +1,4 @@ +use crate::objects::instance::Instance; use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -22,10 +23,14 @@ pub fn waitForReferencePendingList(_: NonNull) { pub fn refersTo0( _: NonNull, - _this: Reference, // java.lang.ref.Reference - _o: Reference, // java.lang.Object + this: Reference, // java.lang.ref.Reference + o: Reference, // java.lang.Object ) -> jboolean { - unimplemented!("java.lang.ref.Reference#refersTo0") + let referent_field_offset = + crate::globals::field_offsets::java_lang_ref_Reference::referent_field_offset(); + let referent = this.get_field_value0(referent_field_offset); + + referent.expect_reference() == o } pub fn clear0(_: NonNull, _this: Reference /* java.lang.ref.Reference */) { diff --git a/runtime/src/native/jdk/internal/util/SystemProps.rs b/runtime/src/native/jdk/internal/util/SystemProps.rs index ac474b7..88f0c74 100644 --- a/runtime/src/native/jdk/internal/util/SystemProps.rs +++ b/runtime/src/native/jdk/internal/util/SystemProps.rs @@ -44,12 +44,14 @@ pub mod Raw { Reference::array(prop_array) } - pub fn platformProperties(_env: NonNull) -> Reference /* [Ljava/lang/String; */ { + pub fn platformProperties(env: NonNull) -> Reference /* [Ljava/lang/String; */ { macro_rules! store_properties { ($prop_array:ident; $($index:expr => $value:expr),+ $(,)?) => { $( - let interned_string = StringInterner::intern_str($value); - $prop_array.store($index, Operand::Reference(Reference::class(interned_string))); + if let Some(val) = Option::::from($value) { + let interned_string = StringInterner::intern_string(val); + $prop_array.store($index, Operand::Reference(Reference::class(interned_string))); + } )+ }; } @@ -59,46 +61,49 @@ pub mod Raw { let prop_array_mut = prop_array.get_mut(); + let mut system_properties = platform::properties::PropertySet::default(); + system_properties.fill().unwrap(); + store_properties!(prop_array_mut; - _display_country_NDX => "TODO", - _display_language_NDX => "TODO", - _display_script_NDX => "TODO", - _display_variant_NDX => "TODO", - _file_encoding_NDX => "TODO", - _file_separator_NDX => "TODO", - _format_country_NDX => "TODO", - _format_language_NDX => "TODO", - _format_script_NDX => "TODO", - _format_variant_NDX => "TODO", - _ftp_nonProxyHosts_NDX => "TODO", - _ftp_proxyHost_NDX => "TODO", - _ftp_proxyPort_NDX => "TODO", - _http_nonProxyHosts_NDX => "TODO", - _http_proxyHost_NDX => "TODO", - _http_proxyPort_NDX => "TODO", - _https_proxyHost_NDX => "TODO", - _https_proxyPort_NDX => "TODO", - _java_io_tmpdir_NDX => "TODO", - _line_separator_NDX => "TODO", - _os_arch_NDX => "TODO", - _os_name_NDX => "TODO", - _os_version_NDX => "TODO", - _path_separator_NDX => "TODO", - _socksNonProxyHosts_NDX => "TODO", - _socksProxyHost_NDX => "TODO", - _socksProxyPort_NDX => "TODO", - _stderr_encoding_NDX => "TODO", - _stdout_encoding_NDX => "TODO", - _sun_arch_abi_NDX => "TODO", - _sun_arch_data_model_NDX => "TODO", - _sun_cpu_endian_NDX => "TODO", - _sun_cpu_isalist_NDX => "TODO", - _sun_io_unicode_encoding_NDX => "TODO", - _sun_jnu_encoding_NDX => "TODO", - _sun_os_patch_level_NDX => "TODO", - _user_dir_NDX => "TODO", - _user_home_NDX => "TODO", - _user_name_NDX => "TODO", + _display_country_NDX => system_properties.display_country, + _display_language_NDX => system_properties.display_language, + _display_script_NDX => system_properties.display_script, + _display_variant_NDX => system_properties.display_variant, + _file_encoding_NDX => system_properties.file_encoding, + _file_separator_NDX => system_properties.file_separator, + _format_country_NDX => system_properties.format_country, + _format_language_NDX => system_properties.format_language, + _format_script_NDX => system_properties.format_script, + _format_variant_NDX => system_properties.format_variant, + _ftp_nonProxyHosts_NDX => system_properties.ftp_nonProxyHosts, + _ftp_proxyHost_NDX => system_properties.ftp_proxyHost, + _ftp_proxyPort_NDX => system_properties.ftp_proxyPort, + _http_nonProxyHosts_NDX => system_properties.http_nonProxyHosts, + _http_proxyHost_NDX => system_properties.http_proxyHost, + _http_proxyPort_NDX => system_properties.http_proxyPort, + _https_proxyHost_NDX => system_properties.https_proxyHost, + _https_proxyPort_NDX => system_properties.https_proxyPort, + _java_io_tmpdir_NDX => system_properties.java_io_tmpdir, + _line_separator_NDX => system_properties.line_separator, + _os_arch_NDX => system_properties.os_arch, + _os_name_NDX => system_properties.os_name, + _os_version_NDX => system_properties.os_version, + _path_separator_NDX => system_properties.path_separator, + _socksNonProxyHosts_NDX => system_properties.socksNonProxyHosts, + _socksProxyHost_NDX => system_properties.socksProxyHost, + _socksProxyPort_NDX => system_properties.socksProxyPort, + _stderr_encoding_NDX => system_properties.stderr_encoding, + _stdout_encoding_NDX => system_properties.stdout_encoding, + _sun_arch_abi_NDX => system_properties.sun_arch_abi, + _sun_arch_data_model_NDX => system_properties.sun_arch_data_model, + _sun_cpu_endian_NDX => system_properties.sun_cpu_endian, + _sun_cpu_isalist_NDX => system_properties.sun_cpu_isalist, + _sun_io_unicode_encoding_NDX => system_properties.sun_io_unicode_encoding, + _sun_jnu_encoding_NDX => system_properties.sun_jnu_encoding, + _sun_os_patch_level_NDX => system_properties.sun_os_patch_level, + _user_dir_NDX => system_properties.user_dir, + _user_home_NDX => system_properties.user_home, + _user_name_NDX => system_properties.user_name, ); Reference::array(prop_array) diff --git a/runtime/src/native/mod.rs b/runtime/src/native/mod.rs index 2e5b36b..07ea50e 100644 --- a/runtime/src/native/mod.rs +++ b/runtime/src/native/mod.rs @@ -86,27 +86,28 @@ pub(self) fn insert_method((def, ptr): (NativeMethodDef, NativeMethodPtr)) { pub(crate) mod java { pub(crate) mod io { - pub(crate) mod FileDescriptor; pub(crate) mod FileInputStream; - pub(crate) mod FileOutputStream; pub(crate) mod UnixFileSystem; + pub(crate) mod FileDescriptor; + pub(crate) mod FileSystem; + pub(crate) mod FileOutputStream; } pub(crate) mod lang { pub(crate) mod r#ref { - pub(crate) mod Finalizer; pub(crate) mod Reference; + pub(crate) mod Finalizer; } - pub(crate) mod Class; - pub(crate) mod ClassLoader; - pub(crate) mod Double; - pub(crate) mod Float; - pub(crate) mod Object; - pub(crate) mod Runtime; pub(crate) mod StringBuilder; + pub(crate) mod Runtime; pub(crate) mod StringUTF16; pub(crate) mod System; - pub(crate) mod Thread; + pub(crate) mod Float; + pub(crate) mod ClassLoader; + pub(crate) mod Double; pub(crate) mod Throwable; + pub(crate) mod Thread; + pub(crate) mod Object; + pub(crate) mod Class; } pub(crate) mod security { pub(crate) mod AccessController; @@ -116,21 +117,22 @@ pub(crate) mod java { pub(crate) mod jdk { pub(crate) mod internal { pub(crate) mod misc { - pub(crate) mod CDS; pub(crate) mod ScopedMemoryAccess; - pub(crate) mod Signal; - pub(crate) mod Unsafe; + pub(crate) mod CDS; pub(crate) mod VM; + pub(crate) mod Unsafe; + pub(crate) mod Signal; } pub(crate) mod util { pub(crate) mod SystemProps; } pub(crate) mod loader { - pub(crate) mod BootLoader; pub(crate) mod NativeLibraries; + pub(crate) mod BootLoader; } pub(crate) mod reflect { pub(crate) mod Reflection; } } } + diff --git a/runtime/src/objects/instance.rs b/runtime/src/objects/instance.rs index c097d59..113e8a4 100644 --- a/runtime/src/objects/instance.rs +++ b/runtime/src/objects/instance.rs @@ -60,7 +60,7 @@ impl Header { // https://github.com/openjdk/jdk/blob/807f6f7fb868240cba5ba117c7059216f69a53f9/src/hotspot/share/runtime/synchronizer.cpp#L935 pub fn generate_hash(&self, thread: &JavaThread) -> jint { if let Some(hash) = self.hash() { - return dbg!(hash); + return hash; } let hash = thread.marsaglia_xor_shift_hash() as jint; diff --git a/symbols/src/lib.rs b/symbols/src/lib.rs index 33eb1c8..77b00e0 100644 --- a/symbols/src/lib.rs +++ b/symbols/src/lib.rs @@ -218,6 +218,7 @@ vm_symbols::define_symbols! { // Fields name: "name", + referent: "referent", loader: "loader", holder: "holder", eetop: "eetop",