From 9bb456b1dcc59687d8bfd17c58fa189e9b0d71e8 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:26:26 -0500 Subject: [PATCH] wip --- .../src/{attribute.rs => attribute/mod.rs} | 8 +- classfile/src/attribute/resolved.rs | 134 ++++++ classfile/src/classfile.rs | 4 +- classfile/src/constant_pool.rs | 8 +- classfile/src/lib.rs | 3 +- classfile/src/methodinfo.rs | 32 +- classfile/src/parse/attributes/annotations.rs | 2 +- classfile/src/parse/mod.rs | 4 +- common/src/lib.rs | 1 + common/src/sync.rs | 19 + jni/src/env.rs | 27 +- platform/src/family/unix/io.rs | 6 + platform/src/family/unix/mod.rs | 1 + platform/src/family/windows/io.rs | 6 + platform/src/family/windows/mod.rs | 1 + runtime/src/globals/classes.rs | 13 + runtime/src/globals/field_offsets.rs | 256 ++++++----- runtime/src/globals/threads.rs | 2 +- runtime/src/initialization.rs | 139 ++++-- runtime/src/interpreter.rs | 187 ++++---- runtime/src/lib.rs | 12 +- runtime/src/method_invoker.rs | 10 +- runtime/src/native/intrinsics.rs | 2 +- runtime/src/native/java/io/FileDescriptor.rs | 33 +- runtime/src/native/java/io/FileInputStream.rs | 15 +- .../src/native/java/io/FileOutputStream.rs | 84 +++- runtime/src/native/java/io/UnixFileSystem.rs | 176 ++++++++ .../src/native/java/io/def/UnixFileSystem.def | 36 ++ runtime/src/native/java/lang/Class.rs | 40 +- runtime/src/native/java/lang/ClassLoader.rs | 2 +- runtime/src/native/java/lang/Object.rs | 8 +- runtime/src/native/java/lang/Runtime.rs | 2 +- runtime/src/native/java/lang/System.rs | 13 +- runtime/src/native/java/lang/Thread.rs | 39 +- runtime/src/native/java/lang/Throwable.rs | 44 +- runtime/src/native/java/lang/ref/Finalizer.rs | 2 +- .../native/java/security/AccessController.rs | 5 +- .../native/jdk/internal/loader/BootLoader.rs | 60 +++ .../jdk/internal/loader/NativeLibraries.rs | 2 +- .../jdk/internal/loader/def/BootLoader.def | 17 + runtime/src/native/jdk/internal/misc/CDS.rs | 2 +- .../jdk/internal/misc/ScopedMemoryAccess.rs | 2 +- .../src/native/jdk/internal/misc/Unsafe.rs | 36 +- runtime/src/native/jdk/internal/misc/VM.rs | 2 +- .../native/jdk/internal/reflect/Reflection.rs | 46 +- .../native/jdk/internal/util/SystemProps.rs | 4 +- runtime/src/native/jni/class.rs | 28 +- runtime/src/native/jni/invocation_api/mod.rs | 9 +- runtime/src/native/jni/mod.rs | 42 +- runtime/src/native/lookup.rs | 8 +- runtime/src/native/mod.rs | 8 +- runtime/src/objects/class/mod.rs | 78 ++-- runtime/src/objects/class/spec.rs | 84 ++-- runtime/src/objects/class_instance.rs | 6 +- runtime/src/objects/constant_pool/cp_types.rs | 253 +++++++++++ runtime/src/objects/constant_pool/entry.rs | 71 +++ runtime/src/objects/constant_pool/mod.rs | 86 ++++ runtime/src/objects/field.rs | 12 +- runtime/src/objects/method/mod.rs | 118 +++-- runtime/src/objects/method/spec.rs | 2 +- runtime/src/objects/mirror.rs | 87 +++- runtime/src/objects/mod.rs | 1 + runtime/src/objects/monitor.rs | 4 +- runtime/src/objects/reference.rs | 16 +- runtime/src/objects/vtable.rs | 2 +- runtime/src/stack/local_stack.rs | 11 +- runtime/src/stack/operand_stack.rs | 13 +- runtime/src/string_interner.rs | 16 +- runtime/src/thread/exceptions.rs | 71 +++ runtime/src/{frame.rs => thread/frame/mod.rs} | 20 +- runtime/src/thread/frame/native.rs | 19 + runtime/src/thread/frame/stack.rs | 128 ++++++ runtime/src/thread/java_lang_Thread.rs | 166 +++++++ runtime/src/{thread.rs => thread/mod.rs} | 410 ++++++------------ runtime/src/thread/pool.rs | 63 +++ runtime/src/verifier/accessors.rs | 6 +- runtime/src/verifier/method.rs | 10 +- runtime/src/verifier/mod.rs | 2 +- symbols/src/lib.rs | 19 + tools/sj/src/cli.rs | 6 +- 80 files changed, 2522 insertions(+), 900 deletions(-) rename classfile/src/{attribute.rs => attribute/mod.rs} (99%) create mode 100644 classfile/src/attribute/resolved.rs create mode 100644 common/src/sync.rs create mode 100644 platform/src/family/unix/io.rs create mode 100644 platform/src/family/windows/io.rs create mode 100644 runtime/src/native/java/io/UnixFileSystem.rs create mode 100644 runtime/src/native/java/io/def/UnixFileSystem.def create mode 100644 runtime/src/native/jdk/internal/loader/BootLoader.rs create mode 100644 runtime/src/native/jdk/internal/loader/def/BootLoader.def create mode 100644 runtime/src/objects/constant_pool/cp_types.rs create mode 100644 runtime/src/objects/constant_pool/entry.rs create mode 100644 runtime/src/objects/constant_pool/mod.rs create mode 100644 runtime/src/thread/exceptions.rs rename runtime/src/{frame.rs => thread/frame/mod.rs} (92%) create mode 100644 runtime/src/thread/frame/native.rs create mode 100644 runtime/src/thread/frame/stack.rs create mode 100644 runtime/src/thread/java_lang_Thread.rs rename runtime/src/{thread.rs => thread/mod.rs} (55%) create mode 100644 runtime/src/thread/pool.rs diff --git a/classfile/src/attribute.rs b/classfile/src/attribute/mod.rs similarity index 99% rename from classfile/src/attribute.rs rename to classfile/src/attribute/mod.rs index c9a3359..5d25d59 100644 --- a/classfile/src/attribute.rs +++ b/classfile/src/attribute/mod.rs @@ -1,3 +1,5 @@ +pub mod resolved; + use common::int_types::{u1, u2}; macro_rules! attribute_getter_methods { @@ -145,8 +147,8 @@ impl From<&[u1]> for AttributeTag { b"PermittedSubclasses" => Self::PermittedSubclasses, _ => unsafe { panic!( - "Encountered unknown attribute type: {}", - std::str::from_utf8_unchecked(bytes) + "Encountered unknown attribute type: {:?}", + std::str::from_utf8(bytes) ); }, } @@ -583,7 +585,7 @@ pub enum ElementValueType { annotation: Annotation, }, Array { - values: Vec + values: Vec }, } diff --git a/classfile/src/attribute/resolved.rs b/classfile/src/attribute/resolved.rs new file mode 100644 index 0000000..6fb41ca --- /dev/null +++ b/classfile/src/attribute/resolved.rs @@ -0,0 +1,134 @@ +use super::{Annotation, ElementValue, ElementValuePair, ElementValueTag, ElementValueType}; +use crate::constant_pool::ConstantPool; + +use common::int_types::{s4, s8, u2}; + +pub struct ResolvedAnnotation { + pub name: String, + pub element_value_pairs: Vec, +} + +impl ResolvedAnnotation { + pub(crate) fn resolve_from(raw_annotation: &Annotation, constant_pool: &ConstantPool) -> Self { + let name = constant_pool.get_constant_utf8(raw_annotation.type_index); + let element_value_pairs = raw_annotation + .element_value_pairs + .iter() + .map(|pair| ResolvedElementValuePair::resolve_from(pair, constant_pool)) + .collect(); + + Self { + name: String::from_utf8(name.to_vec()).unwrap(), + element_value_pairs, + } + } +} + +pub struct ResolvedElementValuePair { + pub element_name: String, + pub value: ResolvedElementValue, +} + +impl ResolvedElementValuePair { + fn resolve_from(raw_value_pair: &ElementValuePair, constant_pool: &ConstantPool) -> Self { + let element_name = constant_pool.get_constant_utf8(raw_value_pair.element_name_index); + let value = ResolvedElementValue::resolve_from(&raw_value_pair.value, constant_pool); + + Self { + element_name: String::from_utf8(element_name.to_vec()).unwrap(), + value, + } + } +} + +pub struct ResolvedElementValue { + pub tag: ElementValueTag, + pub value: ResolvedElementValueType, +} + +impl ResolvedElementValue { + fn resolve_from(raw_element_value: &ElementValue, constant_pool: &ConstantPool) -> Self { + let tag = raw_element_value.tag; + let value = match &raw_element_value.ty { + ElementValueType::Byte { const_value_index } => { + ResolvedElementValueType::Byte(constant_pool.get_integer(*const_value_index)) + }, + ElementValueType::Char { const_value_index } => { + ResolvedElementValueType::Char(constant_pool.get_integer(*const_value_index)) + }, + ElementValueType::Double { const_value_index } => { + ResolvedElementValueType::Double(constant_pool.get_double(*const_value_index)) + }, + ElementValueType::Float { const_value_index } => { + ResolvedElementValueType::Float(constant_pool.get_float(*const_value_index)) + }, + ElementValueType::Int { const_value_index } => { + ResolvedElementValueType::Int(constant_pool.get_integer(*const_value_index)) + }, + ElementValueType::Long { const_value_index } => { + ResolvedElementValueType::Long(constant_pool.get_long(*const_value_index)) + }, + ElementValueType::Short { const_value_index } => { + ResolvedElementValueType::Short(constant_pool.get_integer(*const_value_index)) + }, + ElementValueType::Boolean { const_value_index } => { + ResolvedElementValueType::Boolean(constant_pool.get_integer(*const_value_index)) + }, + + ElementValueType::String { const_value_index } => { + let value = constant_pool.get_constant_utf8(*const_value_index); + ResolvedElementValueType::String(String::from_utf8(value.to_vec()).unwrap()) + }, + ElementValueType::Enum { + type_name_index, + const_value_index, + } => { + let type_name = constant_pool.get_constant_utf8(*type_name_index); + let const_value = constant_pool.get_constant_utf8(*const_value_index); + ResolvedElementValueType::Enum { + type_name: String::from_utf8(type_name.to_vec()).unwrap(), + const_value: String::from_utf8(const_value.to_vec()).unwrap(), + } + }, + ElementValueType::Class { .. } => todo!(), + ElementValueType::Annotation { annotation } => { + let annotation = ResolvedAnnotation::resolve_from(annotation, constant_pool); + ResolvedElementValueType::Annotation { annotation } + }, + ElementValueType::Array { values } => { + let values = values + .iter() + .map(|value| ResolvedElementValue::resolve_from(value, constant_pool)) + .collect(); + ResolvedElementValueType::Array { values } + }, + }; + + Self { tag, value } + } +} + +pub enum ResolvedElementValueType { + Byte(s4), + Char(s4), + Double(f64), + Float(f32), + Int(s4), + Long(s8), + Short(s4), + Boolean(s4), + String(String), + Enum { + type_name: String, + const_value: String, + }, + Class { + class_info_index: u2, + }, + Annotation { + annotation: ResolvedAnnotation, + }, + Array { + values: Vec, + }, +} diff --git a/classfile/src/classfile.rs b/classfile/src/classfile.rs index a5f11a0..acccbe2 100644 --- a/classfile/src/classfile.rs +++ b/classfile/src/classfile.rs @@ -1,6 +1,6 @@ use crate::accessflags::ClassAccessFlags; use crate::attribute::{Attribute, SourceFile}; -use crate::constant_pool::ConstantPoolRef; +use crate::constant_pool::ConstantPool; use crate::fieldinfo::FieldInfo; use crate::methodinfo::MethodInfo; use crate::parse::error::Result; @@ -15,7 +15,7 @@ use common::int_types::{u1, u2}; pub struct ClassFile { pub minor_version: u2, pub major_version: u2, - pub constant_pool: ConstantPoolRef, + pub constant_pool: ConstantPool, pub access_flags: ClassAccessFlags, pub this_class: u2, pub super_class: u2, diff --git a/classfile/src/constant_pool.rs b/classfile/src/constant_pool.rs index debf35a..b4485f6 100644 --- a/classfile/src/constant_pool.rs +++ b/classfile/src/constant_pool.rs @@ -1,13 +1,10 @@ use std::fmt::{Debug, Formatter}; use std::ops::{Deref, Index}; -use std::sync::Arc; use common::int_types::{s4, s8, u1, u2, u4}; // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-4.html#jvms-4.4 -pub type ConstantPoolRef = Arc; - // TODO: Need to make a cache so we don't need to keep resolving classes and interning strings #[derive(PartialEq, Clone)] @@ -27,7 +24,10 @@ impl ConstantPool { self.inner.push(value); } - // TODO: Should return a Symbol + pub fn into_inner(self) -> Vec { + self.inner + } + pub fn get_class_name(&self, idx: u2) -> &[u1] { let constant = &self[idx]; diff --git a/classfile/src/lib.rs b/classfile/src/lib.rs index 5983896..f770dea 100644 --- a/classfile/src/lib.rs +++ b/classfile/src/lib.rs @@ -7,13 +7,14 @@ mod methodinfo; pub mod parse; pub use self::classfile::ClassFile; +pub use attribute::resolved::*; pub use attribute::{ Annotation, Attribute, AttributeTag, AttributeType, BootstrapMethod, Code, CodeException, ElementValue, ElementValuePair, ElementValueTag, ElementValueType, InnerClass, LineNumber, LocalVariable, LocalVariableType, MethodParameter, ModuleExport, ModuleOpen, ModuleProvide, ModuleRequire, RecordComponentInfo, StackMapFrame, StackMapTable, VerificationTypeInfo, }; -pub use constant_pool::{ConstantPool, ConstantPoolRef, ConstantPoolTag, ConstantPoolValueInfo}; +pub use constant_pool::{ConstantPool, ConstantPoolTag, ConstantPoolValueInfo}; pub use fieldinfo::{FieldInfo, FieldType}; pub use methodinfo::{MethodDescriptor, MethodInfo}; pub use parse::error; diff --git a/classfile/src/methodinfo.rs b/classfile/src/methodinfo.rs index 804416d..f07dce9 100644 --- a/classfile/src/methodinfo.rs +++ b/classfile/src/methodinfo.rs @@ -1,10 +1,10 @@ use crate::accessflags::MethodAccessFlags; -use crate::attribute::{Attribute, AttributeType, Code, LineNumberTable}; -use crate::constant_pool::ConstantPoolRef; +use crate::attribute::resolved::ResolvedAnnotation; +use crate::attribute::{Attribute, AttributeType, Code, LineNumber, LineNumberTable}; +use crate::constant_pool::ConstantPool; use crate::error::ClassFileParseError; use crate::fieldinfo::FieldType; use crate::parse::error::Result; -use crate::LineNumber; use common::int_types::{u1, u2}; use common::traits::JavaReadExt; @@ -42,20 +42,24 @@ impl MethodInfo { None } - pub fn is_intrinsic_candidate(&self, constant_pool: ConstantPoolRef) -> bool { - const INTRINSIC_CANDIDATE_TYPE: &[u1] = b"Ljdk/internal/vm/annotation/IntrinsicCandidate;"; - + pub fn runtime_visible_annotations<'a>( + &'a self, + constant_pool: &'a ConstantPool, + ) -> Option + 'a> { for attr in &self.attributes { - if let Some(anno) = attr.runtime_visible_annotations() { - if anno.annotations.iter().any(|anno| { - constant_pool.get_constant_utf8(anno.type_index) == INTRINSIC_CANDIDATE_TYPE - }) { - return true; - } - } + let Some(raw_annotations) = attr.runtime_visible_annotations() else { + continue; + }; + + let iter = raw_annotations + .annotations + .iter() + .map(move |anno| ResolvedAnnotation::resolve_from(anno, constant_pool)); + + return Some(iter); } - false + None } } diff --git a/classfile/src/parse/attributes/annotations.rs b/classfile/src/parse/attributes/annotations.rs index d7eb9d6..b214f30 100644 --- a/classfile/src/parse/attributes/annotations.rs +++ b/classfile/src/parse/attributes/annotations.rs @@ -241,7 +241,7 @@ fn read_element_value_type(reader: &mut R, tag: ElementValueTag, constant_poo let mut values = Vec::with_capacity(num_values as usize); for _ in 0..num_values { - values.push(read_element_value_type(reader, tag, constant_pool)?); + values.push(read_elementvalue(reader, constant_pool)?); } Ok(ElementValueType::Array { values }) diff --git a/classfile/src/parse/mod.rs b/classfile/src/parse/mod.rs index dfc44f2..1eac66f 100644 --- a/classfile/src/parse/mod.rs +++ b/classfile/src/parse/mod.rs @@ -6,7 +6,7 @@ mod methodinfo; use crate::accessflags::ClassAccessFlags; use crate::classfile::ClassFile; -use crate::constant_pool::{ConstantPool, ConstantPoolRef}; +use crate::constant_pool::ConstantPool; use crate::parse::attributes::Location; use crate::parse::error::Result; @@ -69,7 +69,7 @@ where Ok(ClassFile { minor_version, major_version, - constant_pool: ConstantPoolRef::new(constant_pool), + constant_pool, access_flags: ClassAccessFlags::from(access_flags), this_class, super_class, diff --git a/common/src/lib.rs b/common/src/lib.rs index 4257ff4..5512e51 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -3,4 +3,5 @@ pub mod endian; pub mod error; pub mod int_types; pub mod macros; +pub mod sync; pub mod traits; diff --git a/common/src/sync.rs b/common/src/sync.rs new file mode 100644 index 0000000..03cbe62 --- /dev/null +++ b/common/src/sync.rs @@ -0,0 +1,19 @@ +use std::ops::Deref; + +pub struct ForceSync(pub T); + +unsafe impl Sync for ForceSync {} + +impl ForceSync { + pub const fn new(value: T) -> Self { + ForceSync(value) + } +} + +impl Deref for ForceSync { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/jni/src/env.rs b/jni/src/env.rs index a2a7fe2..acfc6b7 100644 --- a/jni/src/env.rs +++ b/jni/src/env.rs @@ -2,7 +2,7 @@ use crate::error::{JniError, Result}; use crate::objects::{JClass, JMethodId, JObject, JObjectArray, JString, JValue}; use crate::string::JCesu8String; use crate::version::JniVersion; -use jni_sys::jsize; +use jni_sys::{jsize, JNI_TRUE}; /// Safer wrapper around `jni_sys::JNIEnv` #[repr(transparent)] @@ -71,6 +71,31 @@ impl JniEnv { Ok(unsafe { JClass::from_raw(ret) }) } + /// Determines whether an object of `sub` can be safely cast to `sup`. + /// + /// ## PARAMETERS + /// + /// `sub`: the first class argument. + /// + /// `sup`: the second class argument. + /// + /// ## RETURNS + /// + /// Returns `true` if any of the following are true: + /// + /// * `sub` and `sup` refer to the same Java class. + /// * `sub` is a subclass of `sup`. + /// * `sub` has `sup` as one of its interfaces. + pub fn is_assignable_from(&self, sub: JClass, sup: JClass) -> bool { + let ret; + unsafe { + let invoke_interface = self.as_native_interface(); + ret = ((*invoke_interface).IsAssignableFrom)(self.0 as _, sub.raw(), sup.raw()); + } + + ret == JNI_TRUE + } + /// Returns the method ID for a static method of a class. /// /// # Parameters diff --git a/platform/src/family/unix/io.rs b/platform/src/family/unix/io.rs new file mode 100644 index 0000000..a2a2b0e --- /dev/null +++ b/platform/src/family/unix/io.rs @@ -0,0 +1,6 @@ +use std::fs::File; +use std::os::unix::fs::FileExt; + +pub fn write_at(file: &mut File, content: &[u8], offset: u64) -> std::io::Result { + file.write_at(content, offset) +} diff --git a/platform/src/family/unix/mod.rs b/platform/src/family/unix/mod.rs index 03ca603..0760fa9 100644 --- a/platform/src/family/unix/mod.rs +++ b/platform/src/family/unix/mod.rs @@ -18,5 +18,6 @@ match_cfg_meta! { // Exports +pub mod io; pub mod properties; pub(super) mod signals; diff --git a/platform/src/family/windows/io.rs b/platform/src/family/windows/io.rs new file mode 100644 index 0000000..2794944 --- /dev/null +++ b/platform/src/family/windows/io.rs @@ -0,0 +1,6 @@ +use std::fs::File; +use std::os::windows::fs::FileExt; + +pub fn write_at(file: &mut File, content: &[u8], offset: u64) -> std::io::Result { + file.seek_write(content, offset) +} diff --git a/platform/src/family/windows/mod.rs b/platform/src/family/windows/mod.rs index 25286a9..3f09290 100644 --- a/platform/src/family/windows/mod.rs +++ b/platform/src/family/windows/mod.rs @@ -1,2 +1,3 @@ +pub mod io; pub mod properties; pub(super) mod signals; diff --git a/runtime/src/globals/classes.rs b/runtime/src/globals/classes.rs index 4fb8ae0..528309b 100644 --- a/runtime/src/globals/classes.rs +++ b/runtime/src/globals/classes.rs @@ -46,10 +46,23 @@ define_classes!( java_lang_ThreadGroup, java_lang_Throwable, java_lang_Cloneable, + java_lang_Module, java_lang_ref_Finalizer, java_io_FileDescriptor, java_io_FileInputStream, java_io_FileOutputStream, + java_io_File, + jdk_internal_reflect_MethodAccessorImpl, + // Primitive types + java_lang_Boolean, + java_lang_Byte, + java_lang_Character, + java_lang_Double, + java_lang_Float, + java_lang_Integer, + java_lang_Long, + java_lang_Short, + java_lang_Void, // Primitive arrays bool_array, byte_array, diff --git a/runtime/src/globals/field_offsets.rs b/runtime/src/globals/field_offsets.rs index 20c5fb4..eddbbbf 100644 --- a/runtime/src/globals/field_offsets.rs +++ b/runtime/src/globals/field_offsets.rs @@ -1,111 +1,167 @@ -//! Various offsets for fields of frequently accessed classes - -static mut STRING_VALUE_FIELD_OFFSET: usize = 0; -static mut STRING_CODER_FIELD_OFFSET: usize = 0; - -/// `java.lang.String#value` field offset -/// -/// This will not change for the lifetime of the program. -/// -/// Expected type: `jByteArray` -pub fn string_value_field_offset() -> usize { - unsafe { STRING_VALUE_FIELD_OFFSET } -} -/// `java.lang.String#coder` field offset -/// -/// This will not change for the lifetime of the program. -/// -/// Expected type: `jint` -pub fn string_coder_field_offset() -> usize { - unsafe { STRING_CODER_FIELD_OFFSET } -} - -pub unsafe fn set_string_field_offsets(value: usize, coder: usize) { - STRING_VALUE_FIELD_OFFSET = value; - STRING_CODER_FIELD_OFFSET = coder; -} - -static mut CLASS_NAME_FIELD_OFFSET: usize = 0; - -pub fn class_name_field_offset() -> usize { - unsafe { CLASS_NAME_FIELD_OFFSET } -} - -static mut THREAD_HOLDER_FIELD_OFFSET: usize = 0; +#![allow(non_snake_case)] -/// `java.lang.Thread#holder` field offset -/// -/// This will not change for the lifetime of the program. -/// -/// Expected type: `java.lang.Thread$FieldHolder` reference -pub fn thread_holder_field_offset() -> usize { - unsafe { THREAD_HOLDER_FIELD_OFFSET } -} - -pub unsafe fn set_thread_holder_field_offset(value: usize) { - THREAD_HOLDER_FIELD_OFFSET = value; -} - -static mut FIELDHOLDER_PRIORITY_FIELD_OFFSET: usize = 0; -static mut FIELDHOLDER_DAEMON_FIELD_OFFSET: usize = 0; -static mut FIELDHOLDER_THREAD_STATUS_FIELD_OFFSET: usize = 0; - -/// `java.lang.Thread$FieldHolder#priority` field offset -/// -/// **THIS IS A STATIC FIELD** -/// -/// This will not change for the lifetime of the program. -/// -/// Expected field type: `jint` -pub fn field_holder_priority_field_offset() -> usize { - unsafe { FIELDHOLDER_PRIORITY_FIELD_OFFSET } -} - -pub unsafe fn set_field_holder_priority_field_offset(value: usize) { - FIELDHOLDER_PRIORITY_FIELD_OFFSET = value; -} +//! Various offsets for fields of frequently accessed classes -/// `java.lang.Thread$FieldHolder#daemon` field offset -/// -/// **THIS IS A STATIC FIELD** -/// -/// This will not change for the lifetime of the program. -/// -/// Expected field type: `jboolean` -pub fn field_holder_daemon_field_offset() -> usize { - unsafe { FIELDHOLDER_DAEMON_FIELD_OFFSET } -} +pub mod java_lang_Class { + static mut CLASS_NAME_FIELD_OFFSET: usize = 0; -pub unsafe fn set_field_holder_daemon_field_offset(value: usize) { - FIELDHOLDER_DAEMON_FIELD_OFFSET = value; -} + pub fn name_field_offset() -> usize { + unsafe { CLASS_NAME_FIELD_OFFSET } + } -/// `java.lang.Thread$FieldHolder#threadStatus` field offset -/// -/// **THIS IS A STATIC FIELD** -/// -/// This will not change for the lifetime of the program. -/// -/// Expected field type: `jint` -pub fn field_holder_thread_status_field_offset() -> usize { - unsafe { FIELDHOLDER_THREAD_STATUS_FIELD_OFFSET } + pub unsafe fn set_name_field_offset(value: usize) { + CLASS_NAME_FIELD_OFFSET = value; + } } -pub unsafe fn set_field_holder_thread_status_field_offset(value: usize) { - FIELDHOLDER_THREAD_STATUS_FIELD_OFFSET = value; +pub mod java_lang_String { + static mut STRING_VALUE_FIELD_OFFSET: usize = 0; + static mut STRING_CODER_FIELD_OFFSET: usize = 0; + + /// `java.lang.String#value` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected type: `jByteArray` + pub fn value_field_offset() -> usize { + unsafe { STRING_VALUE_FIELD_OFFSET } + } + /// `java.lang.String#coder` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected type: `jint` + pub fn coder_field_offset() -> usize { + unsafe { STRING_CODER_FIELD_OFFSET } + } + + pub unsafe fn set_field_offsets(value: usize, coder: usize) { + STRING_VALUE_FIELD_OFFSET = value; + STRING_CODER_FIELD_OFFSET = coder; + } } -static mut THREAD_EETOP_FIELD_OFFSET: usize = 0; - -/// `java.lang.Thread#eetop` field offset -/// -/// This will not change for the lifetime of the program. -/// -/// Expected type: `jlong` -pub fn thread_eetop_field_offset() -> usize { - unsafe { THREAD_EETOP_FIELD_OFFSET } +pub mod java_lang_Module { + static mut MODULE_NAME_FIELD_OFFSET: usize = 0; + static mut MODULE_LOADER_FIELD_OFFSET: usize = 0; + + /// `java.lang.Module#name` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected type: `Reference` to `java.lang.String` + pub fn name_field_offset() -> usize { + unsafe { MODULE_NAME_FIELD_OFFSET } + } + + pub unsafe fn set_name_field_offset(value: usize) { + MODULE_NAME_FIELD_OFFSET = value; + } + + /// `java.lang.Module#loader` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected type: `Reference` to `java.lang.ClassLoader` + pub fn loader_field_offset() -> usize { + unsafe { MODULE_LOADER_FIELD_OFFSET } + } + + pub unsafe fn set_loader_field_offset(value: usize) { + MODULE_LOADER_FIELD_OFFSET = value; + } } -pub unsafe fn set_thread_eetop_field_offset(value: usize) { - THREAD_EETOP_FIELD_OFFSET = value; +pub mod java_lang_Thread { + static mut THREAD_EETOP_FIELD_OFFSET: usize = 0; + static mut THREAD_HOLDER_FIELD_OFFSET: usize = 0; + + /// `java.lang.Thread#eetop` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected type: `jlong` + pub fn eetop_field_offset() -> usize { + unsafe { THREAD_EETOP_FIELD_OFFSET } + } + + pub unsafe fn set_eetop_field_offset(value: usize) { + THREAD_EETOP_FIELD_OFFSET = value; + } + + /// `java.lang.Thread#holder` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected type: `java.lang.Thread$FieldHolder` reference + pub fn holder_field_offset() -> usize { + unsafe { THREAD_HOLDER_FIELD_OFFSET } + } + + pub unsafe fn set_holder_field_offset(value: usize) { + THREAD_HOLDER_FIELD_OFFSET = value; + } + + pub mod holder { + static mut FIELDHOLDER_STACK_SIZE_FIELD_OFFSET: usize = 0; + static mut FIELDHOLDER_PRIORITY_FIELD_OFFSET: usize = 0; + static mut FIELDHOLDER_DAEMON_FIELD_OFFSET: usize = 0; + static mut FIELDHOLDER_THREAD_STATUS_FIELD_OFFSET: usize = 0; + + /// `java.lang.Thread$FieldHolder#stackSize` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected field type: `jint` + pub fn stack_size_field_offset() -> usize { + unsafe { FIELDHOLDER_STACK_SIZE_FIELD_OFFSET } + } + + pub unsafe fn set_stack_size_field_offset(value: usize) { + FIELDHOLDER_STACK_SIZE_FIELD_OFFSET = value; + } + + /// `java.lang.Thread$FieldHolder#priority` field offset + /// + /// This will not change for the lifetime of the program. + /// + /// Expected field type: `jint` + pub fn priority_field_offset() -> usize { + unsafe { FIELDHOLDER_PRIORITY_FIELD_OFFSET } + } + + pub unsafe fn set_priority_field_offset(value: usize) { + FIELDHOLDER_PRIORITY_FIELD_OFFSET = value; + } + + /// `java.lang.Thread$FieldHolder#daemon` field offset + /// + /// **THIS IS A STATIC FIELD** + /// + /// This will not change for the lifetime of the program. + /// + /// Expected field type: `jboolean` + pub fn daemon_field_offset() -> usize { + unsafe { FIELDHOLDER_DAEMON_FIELD_OFFSET } + } + + pub unsafe fn set_daemon_field_offset(value: usize) { + FIELDHOLDER_DAEMON_FIELD_OFFSET = value; + } + + /// `java.lang.Thread$FieldHolder#threadStatus` field offset + /// + /// **THIS IS A STATIC FIELD** + /// + /// This will not change for the lifetime of the program. + /// + /// Expected field type: `jint` + pub fn thread_status_field_offset() -> usize { + unsafe { FIELDHOLDER_THREAD_STATUS_FIELD_OFFSET } + } + + pub unsafe fn set_thread_status_field_offset(value: usize) { + FIELDHOLDER_THREAD_STATUS_FIELD_OFFSET = value; + } + } } diff --git a/runtime/src/globals/threads.rs b/runtime/src/globals/threads.rs index da8bb0a..7ab8c39 100644 --- a/runtime/src/globals/threads.rs +++ b/runtime/src/globals/threads.rs @@ -1,4 +1,4 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::cell::SyncUnsafeCell; use std::mem::MaybeUninit; diff --git a/runtime/src/initialization.rs b/runtime/src/initialization.rs index e821734..ba83ef7 100644 --- a/runtime/src/initialization.rs +++ b/runtime/src/initialization.rs @@ -1,10 +1,10 @@ -use crate::class_instance::ClassInstance; use crate::classpath::classloader::ClassLoader; use crate::java_call; use crate::native::jni::invocation_api::main_java_vm; -use crate::reference::Reference; +use crate::objects::class_instance::ClassInstance; +use crate::objects::reference::Reference; use crate::string_interner::StringInterner; -use crate::thread::{java_lang_Thread, JavaThread}; +use crate::thread::{java_lang_Thread, JavaThread, JavaThreadBuilder}; use classfile::accessflags::MethodAccessFlags; use classfile::FieldType; @@ -15,7 +15,7 @@ use jni::sys::JavaVMInitArgs; use symbols::sym; pub fn create_java_vm(args: Option<&JavaVMInitArgs>) -> JavaVm { - let thread = JavaThread::new(); + let thread = JavaThreadBuilder::new().finish(); unsafe { JavaThread::set_current_thread(thread); } @@ -28,37 +28,7 @@ fn initialize_thread(thread: &JavaThread) { // Load some important classes first load_global_classes(); - // Grab the java.lang.String field offsets - { - let string_class = crate::globals::classes::java_lang_String(); - let string_value_field = string_class - .fields() - .find(|field| { - !field.is_static() - && field.name == sym!(value) - && matches!(field.descriptor, FieldType::Array(ref val) if **val == FieldType::Byte) - }) - .expect("java.lang.String should have a value field"); - - let string_coder_field = string_class - .fields() - .find(|field| { - field.is_final() - && field.name == sym!(coder) - && matches!(field.descriptor, FieldType::Byte) - }) - .expect("java.lang.String should have a value field"); - - unsafe { - crate::globals::field_offsets::set_string_field_offsets( - string_value_field.idx, - string_coder_field.idx, - ); - } - } - - // Grab the java.lang.Thread field offsets - java_lang_Thread::set_field_offsets(); + init_field_offsets(); // Init some important classes initialize_global_classes(thread); @@ -105,12 +75,27 @@ fn load_global_classes() { ClassLoader::fixup_mirrors(); load!( + java_lang_Module, java_lang_Thread, java_lang_Thread_FieldHolder, java_lang_ThreadGroup, java_lang_Throwable, java_lang_Cloneable, java_lang_ref_Finalizer, + jdk_internal_reflect_MethodAccessorImpl, + ); + + // Primitive types + load!( + java_lang_Boolean, + java_lang_Byte, + java_lang_Character, + java_lang_Double, + java_lang_Float, + java_lang_Integer, + java_lang_Long, + java_lang_Short, + java_lang_Void, ); // Primitive arrays @@ -126,6 +111,90 @@ fn load_global_classes() { ) } +fn init_field_offsets() { + // java.lang.Class + { + let class_class = crate::globals::classes::java_lang_Class(); + let class_name_field = class_class + .fields() + .find(|field| { + !field.is_static() + && field.name == sym!(name) + && matches!(field.descriptor, FieldType::Object(ref val) if **val == *b"java/lang/String") + }) + .expect("java.lang.Class should have a name field"); + + unsafe { + crate::globals::field_offsets::java_lang_Class::set_name_field_offset( + class_name_field.idx, + ); + } + } + + // java.lang.String + { + let string_class = crate::globals::classes::java_lang_String(); + let string_value_field = string_class + .fields() + .find(|field| { + !field.is_static() + && field.name == sym!(value) + && matches!(field.descriptor, FieldType::Array(ref val) if **val == FieldType::Byte) + }) + .expect("java.lang.String should have a value field"); + + let string_coder_field = string_class + .fields() + .find(|field| { + field.is_final() + && field.name == sym!(coder) + && matches!(field.descriptor, FieldType::Byte) + }) + .expect("java.lang.String should have a value field"); + + unsafe { + crate::globals::field_offsets::java_lang_String::set_field_offsets( + string_value_field.idx, + string_coder_field.idx, + ); + } + } + + // java.lang.Module + { + let module_class = crate::globals::classes::java_lang_Module(); + let module_name_field = module_class + .fields() + .find(|field| { + !field.is_static() + && field.name == sym!(name) + && matches!(field.descriptor, FieldType::Object(ref val) if **val == *b"java/lang/String") + }) + .expect("java.lang.Module should have a name field"); + + let module_loader_field = module_class + .fields() + .find(|field| { + !field.is_static() + && field.name == sym!(loader) + && matches!(field.descriptor, FieldType::Object(ref val) if **val == *b"java/lang/ClassLoader") + }) + .expect("java.lang.Module should have a loader field"); + + unsafe { + crate::globals::field_offsets::java_lang_Module::set_name_field_offset( + module_name_field.idx, + ); + crate::globals::field_offsets::java_lang_Module::set_loader_field_offset( + module_loader_field.idx, + ); + } + } + + // java.lang.Thread + java_lang_Thread::set_field_offsets(); +} + fn initialize_global_classes(thread: &JavaThread) { crate::globals::classes::java_lang_Object().initialize(thread); crate::globals::classes::java_lang_Class().initialize(thread); diff --git a/runtime/src/interpreter.rs b/runtime/src/interpreter.rs index 324e5c0..baa5bde 100644 --- a/runtime/src/interpreter.rs +++ b/runtime/src/interpreter.rs @@ -1,21 +1,21 @@ #![allow(unused_imports)] // Intellij-Rust doesn't like this file much, the imports used in macros are not recognized -use crate::class::{Class, ClassInitializationState}; -use crate::class_instance::{ArrayInstance, ClassInstance}; use crate::classpath::classloader::ClassLoader; -use crate::frame::Frame; -use crate::method::Method; use crate::method_invoker::MethodInvoker; -use crate::objects::class_instance::Instance; -use crate::reference::{ClassInstanceRef, Reference}; +use crate::objects::class::{Class, ClassInitializationState}; +use crate::objects::class_instance::{ArrayInstance, ClassInstance, Instance}; +use crate::objects::constant_pool::cp_types::{self, Entry}; +use crate::objects::field::Field; +use crate::objects::method::Method; +use crate::objects::reference::{ClassInstanceRef, Reference}; use crate::string_interner::StringInterner; +use crate::thread::frame::Frame; use crate::thread::JavaThread; use std::cmp::Ordering; use std::sync::atomic::Ordering as MemOrdering; use std::sync::Arc; -use crate::field::Field; use classfile::ConstantPoolValueInfo; use common::int_types::{s2, s4, s8, u2}; use common::traits::PtrType; @@ -564,75 +564,67 @@ impl Interpreter { // invokedynamic CATEGORY: references OpCode::getstatic => { - if let Some(field) = Self::fetch_field(frame, true) { - frame.stack_mut().push_op(field.get_static_value()); - } + let field = Self::fetch_field(frame, true); + frame.stack_mut().push_op(field.get_static_value()); }, OpCode::putstatic => { - if let Some(field) = Self::fetch_field(frame, true) { - let value = frame.stack_mut().pop(); + let field = Self::fetch_field(frame, true); + let value = frame.stack_mut().pop(); - field.set_static_value(value); - } + field.set_static_value(value); }, OpCode::getfield => { - if let Some(field) = Self::fetch_field(frame, false) { - if field.is_static() { - panic!("IncompatibleClassChangeError"); // TODO - } + let field = Self::fetch_field(frame, false); + if field.is_static() { + panic!("IncompatibleClassChangeError"); // TODO + } - let stack = frame.stack_mut(); + let stack = frame.stack_mut(); - let object_ref = stack.pop_reference(); + let object_ref = stack.pop_reference(); - let field_value = object_ref.get_field_value(field); - stack.push_op(field_value); - } + let field_value = object_ref.get_field_value(field); + stack.push_op(field_value); }, OpCode::putfield => { - if let Some(field) = Self::fetch_field(frame, false) { - if field.is_static() { - panic!("IncompatibleClassChangeError"); // TODO - } + let field = Self::fetch_field(frame, false); + if field.is_static() { + panic!("IncompatibleClassChangeError"); // TODO + } - // TODO: if the resolved field is final, it must be declared in the current class, - // and the instruction must occur in an instance initialization method of the current class. - // Otherwise, an IllegalAccessError is thrown. + // TODO: if the resolved field is final, it must be declared in the current class, + // and the instruction must occur in an instance initialization method of the current class. + // Otherwise, an IllegalAccessError is thrown. - let stack = frame.stack_mut(); + let stack = frame.stack_mut(); - let value = stack.pop(); - let mut object_ref = stack.pop_reference(); + let value = stack.pop(); + let mut object_ref = stack.pop_reference(); - object_ref.put_field_value(field, value); - } + object_ref.put_field_value(field, value); }, OpCode::invokevirtual => { - if let Some(method) = Self::fetch_method(frame, false) { - MethodInvoker::invoke_virtual(frame, method); - } + let method = Self::fetch_method(frame, false); + MethodInvoker::invoke_virtual(frame, method); }, OpCode::invokespecial => { - if let Some(method) = Self::fetch_method(frame, false) { - MethodInvoker::invoke(frame, method); - } + let method = Self::fetch_method(frame, false); + MethodInvoker::invoke(frame, method); }, OpCode::invokestatic => { - if let Some(method) = Self::fetch_method(frame, true) { - MethodInvoker::invoke(frame, method); - } + let method = Self::fetch_method(frame, true); + MethodInvoker::invoke(frame, method); }, OpCode::invokeinterface => { - if let Some(method) = Self::fetch_method(frame, false) { - // The count operand is an unsigned byte that must not be zero. - let count = frame.read_byte(); - assert!(count > 0); + let method = Self::fetch_method(frame, false); + // The count operand is an unsigned byte that must not be zero. + let count = frame.read_byte(); + assert!(count > 0); - // The value of the fourth operand byte must always be zero. - assert_eq!(frame.read_byte(), 0); + // The value of the fourth operand byte must always be zero. + assert_eq!(frame.read_byte(), 0); - MethodInvoker::invoke_interface(frame, method); - } + MethodInvoker::invoke_interface(frame, method); }, OpCode::new => { let new_class_instance = Self::new(frame); @@ -650,15 +642,12 @@ impl Interpreter { OpCode::anewarray => { let index = frame.read_byte2(); - let method = frame.method(); - - let constant_pool = &method.class.unwrap_class_instance().constant_pool; - let array_class_name = constant_pool.get_class_name(index); + let constant_pool = frame.constant_pool(); + let array_class = constant_pool.get::(index); let stack = frame.stack_mut(); let count = stack.pop_int(); - - let array_class = ClassLoader::Bootstrap.load(Symbol::intern_bytes(array_class_name)).unwrap(); + let array_ref = ArrayInstance::new_reference(count, array_class); stack.push_reference(Reference::array(array_ref)); }, @@ -761,45 +750,38 @@ impl Interpreter { }; let constant_pool = frame.constant_pool(); - let constant = &constant_pool[idx]; + let constant = constant_pool.get_any(idx); // The run-time constant pool entry at index must be loadable (§5.1), match constant { // and not any of the following: - ConstantPoolValueInfo::Long { .. } - | ConstantPoolValueInfo::Double { .. } => panic!("ldc called with index to long/double"), + Entry::Long { .. } + | Entry::Double { .. } => panic!("ldc called with index to long/double"), // If the run-time constant pool entry is a numeric constant of type int or float, // then the value of that numeric constant is pushed onto the operand stack as an int or float, respectively. - ConstantPoolValueInfo::Integer { bytes } => frame.stack_mut().push_int((*bytes) as s4), - ConstantPoolValueInfo::Float { bytes } => frame.stack_mut().push_float(f32::from_be_bytes(bytes.to_be_bytes())), + Entry::Integer(int) => frame.stack_mut().push_int(int), + Entry::Float(float) => frame.stack_mut().push_float(float), // Otherwise, if the run-time constant pool entry is a string constant, that is, // a reference to an instance of class String, then value, a reference to that instance, is pushed onto the operand stack. - ConstantPoolValueInfo::String { string_index } => { - let bytes = constant_pool.get_constant_utf8(*string_index); - let interned_string = StringInterner::intern_bytes(bytes); - + Entry::String(string) => { + let interned_string = StringInterner::intern_symbol(string); frame.stack_mut().push_reference(Reference::class(interned_string)); }, // Otherwise, if the run-time constant pool entry is a symbolic reference to a class or interface, // then the named class or interface is resolved (§5.4.3.1) and value, a reference to the Class object // representing that class or interface, is pushed onto the operand stack. - ConstantPoolValueInfo::Class { name_index } => { - let class = frame.method().class; - - let class_name = constant_pool.get_constant_utf8(*name_index); - let classref = class.loader.load(Symbol::intern_bytes(class_name)).unwrap(); - - frame.stack_mut().push_reference(Reference::mirror(classref.mirror())); + Entry::Class(class) => { + frame.stack_mut().push_reference(Reference::mirror(class.mirror())); }, // Otherwise, the run-time constant pool entry is a symbolic reference to a method type, a method handle, // or a dynamically-computed constant. The symbolic reference is resolved (§5.4.3.5, §5.4.3.6) and value, // the result of resolution, is pushed onto the operand stack. - ConstantPoolValueInfo::MethodHandle { .. } => unimplemented!("MethodHandle in ldc"), - ConstantPoolValueInfo::MethodType { .. } => unimplemented!("MethodType in ldc"), + Entry::MethodHandle { .. } => unimplemented!("MethodHandle in ldc"), + Entry::MethodType { .. } => unimplemented!("MethodType in ldc"), _ => unreachable!() } } @@ -809,21 +791,16 @@ impl Interpreter { let idx = frame.read_byte2(); let constant_pool = frame.constant_pool(); - let constant = &constant_pool[idx]; + let constant = constant_pool.get_any(idx); // The run-time constant pool entry at index must be loadable (§5.1), match constant { // and not any of the following: - ConstantPoolValueInfo::Long { high_bytes, low_bytes } => { - frame.stack_mut().push_long((s8::from(*high_bytes) << 32) + s8::from(*low_bytes)) + Entry::Long(long) => { + frame.stack_mut().push_long(long) }, - ConstantPoolValueInfo::Double { high_bytes, low_bytes } => { - let high = high_bytes.to_be_bytes(); - let low = low_bytes.to_be_bytes(); - - frame.stack_mut().push_double(f64::from_be_bytes([ - high[0], high[1], high[2], high[3], low[0], low[1], low[2], low[3], - ])) + Entry::Double(double) => { + frame.stack_mut().push_double(double) }, _ => panic!("ldc2_w called with index to non long/double constant") @@ -907,15 +884,11 @@ impl Interpreter { } } - let method = frame.method(); - let class_ref = &method.class; - - let constant_pool = &class_ref.unwrap_class_instance().constant_pool; - let class_name = constant_pool.get_class_name(index); - let resolved_class = class_ref.loader.load(Symbol::intern_bytes(class_name)).unwrap(); + let constant_pool = frame.constant_pool(); + let class = constant_pool.get::(index); let stack = frame.stack_mut(); - if objectref.is_instance_of(resolved_class) { + if objectref.is_instance_of(class) { match opcode { // If objectref is an instance of the resolved class or array type, or implements the resolved interface, // the instanceof instruction pushes an int result of 1 as an int onto the operand stack @@ -935,33 +908,28 @@ impl Interpreter { } } - fn fetch_field(frame: &mut Frame, is_static: bool) -> Option<&'static Field> { + fn fetch_field(frame: &mut Frame, is_static: bool) -> &'static Field { let field_ref_idx = frame.read_byte2(); + let class = frame.method().class(); let constant_pool = frame.constant_pool(); - let (class_name_index, _) = constant_pool.get_field_ref(field_ref_idx); - let class_name = constant_pool.get_class_name(class_name_index); - let class = ClassLoader::Bootstrap - .load(Symbol::intern_bytes(class_name)) - .unwrap(); - - let ret = class.resolve_field(constant_pool, field_ref_idx); - if ret.is_some() && is_static { + let ret = constant_pool.get::(field_ref_idx); + if is_static { class.initialize(frame.thread()); } ret } - fn fetch_method(frame: &mut Frame, is_static: bool) -> Option<&'static Method> { + fn fetch_method(frame: &mut Frame, is_static: bool) -> &'static Method { let method_ref_idx = frame.read_byte2(); - let method = frame.method(); - let class = method.class; + let class = frame.method().class(); + let constant_pool = frame.constant_pool(); - let ret = class.resolve_method(frame.thread(), method_ref_idx); - if ret.is_some() && is_static { + let ret = constant_pool.get::(method_ref_idx); + if is_static { // On successful resolution of the method, the class or interface that declared the resolved method is initialized if that class or interface has not already been initialized class.initialize(frame.thread()); } @@ -972,12 +940,9 @@ impl Interpreter { fn new(frame: &mut Frame) -> ClassInstanceRef { let index = frame.read_byte2(); - let class = &frame.method().class; let constant_pool = frame.constant_pool(); - let class_name = constant_pool.get_class_name(index); - let class = class.loader.load(Symbol::intern_bytes(class_name)).unwrap(); - + let class = constant_pool.get::(index); if class.is_interface() || class.is_abstract() { panic!("InstantiationError") // TODO } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index dd3f5bd..3a107a8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -5,23 +5,21 @@ #![feature(macro_metavar_expr)] #![feature(specialization)] #![feature(sync_unsafe_cell)] +#![feature(core_intrinsics)] +#![feature(try_with_capacity)] pub mod calls; pub mod classpath; -mod error; -mod frame; +pub mod error; pub mod globals; mod initialization; mod interpreter; mod method_invoker; pub mod native; -mod objects; +pub mod objects; pub mod stack; mod string_interner; -mod thread; +pub mod thread; pub mod verifier; -pub use frame::Frame; pub use interpreter::Interpreter; -pub use objects::*; -pub use thread::{JVMOptions, JavaThread}; diff --git a/runtime/src/method_invoker.rs b/runtime/src/method_invoker.rs index 71666c4..e379542 100644 --- a/runtime/src/method_invoker.rs +++ b/runtime/src/method_invoker.rs @@ -1,8 +1,8 @@ -use crate::frame::Frame; -use crate::method::Method; -use crate::reference::Reference; +use crate::objects::method::Method; +use crate::objects::reference::Reference; use crate::stack::local_stack::LocalStack; -use crate::JavaThread; +use crate::thread::frame::Frame; +use crate::thread::JavaThread; use common::int_types::{u1, u2}; use instructions::{Operand, StackLike}; @@ -81,7 +81,7 @@ impl MethodInvoker { if this.is_null() { panic!( "NullPointerException - {}#{}", - method.class.name.as_str(), + method.class().name.as_str(), method.name.as_str() ); } diff --git a/runtime/src/native/intrinsics.rs b/runtime/src/native/intrinsics.rs index 81c7489..36472f6 100644 --- a/runtime/src/native/intrinsics.rs +++ b/runtime/src/native/intrinsics.rs @@ -1,4 +1,4 @@ -use crate::method::Method; +use crate::objects::method::Method; use classfile::accessflags::MethodAccessFlags; // static REGISTERED_INTRINSICS: Lazy>> = diff --git a/runtime/src/native/java/io/FileDescriptor.rs b/runtime/src/native/java/io/FileDescriptor.rs index 99ce87a..746b16a 100644 --- a/runtime/src/native/java/io/FileDescriptor.rs +++ b/runtime/src/native/java/io/FileDescriptor.rs @@ -1,25 +1,38 @@ #![allow(non_upper_case_globals)] use crate::classpath::classloader::ClassLoader; -use crate::reference::Reference; +use crate::native::jni::jfieldid_from_field_ref; +use crate::objects::class_instance::Instance; +use crate::objects::reference::Reference; use std::cell::SyncUnsafeCell; -use std::ptr::NonNull; +use std::ptr::{self, NonNull}; use std::sync::atomic::{AtomicBool, Ordering}; use ::jni::env::JniEnv; -use ::jni::sys::{jboolean, jint, jlong}; +use ::jni::sys::{jboolean, jfieldID, jint, jlong}; +use common::sync::ForceSync; use symbols::sym; include_generated!("native/java/io/def/FileDescriptor.definitions.rs"); /// `java.io.FileDescriptor#fd` field offset -static fd: SyncUnsafeCell = SyncUnsafeCell::new(0); +static fd: SyncUnsafeCell> = + SyncUnsafeCell::new(ForceSync::new(ptr::null_mut() as _)); +pub fn get_fd(this: &Reference) -> jint { + let fd_value = unsafe { &*fd.get() }; + let field = unsafe { crate::native::jni::field_ref_from_jfieldid(fd_value.0) } + .expect("field should always be present"); + this.get_field_value(field).expect_int() +} + /// `java.io.FileDescriptor#handle` field offset #[cfg(windows)] -static handle: SyncUnsafeCell = SyncUnsafeCell::new(0); +static handle: SyncUnsafeCell> = + SyncUnsafeCell::new(ForceSync::new(ptr::null_mut() as _)); /// `java.io.FileDescriptor#append` field offset -static append: SyncUnsafeCell = SyncUnsafeCell::new(0); +static append: SyncUnsafeCell> = + SyncUnsafeCell::new(ForceSync::new(ptr::null_mut() as _)); // throws SyncFailedException pub fn sync0(_: NonNull, _this: Reference) { @@ -42,20 +55,20 @@ pub fn initIDs(_: NonNull) { } let mut fields = 0; - for (index, field) in class.fields().enumerate() { + for field in class.fields() { match field.name.as_str() { "fd" => unsafe { assert!(fields & 1 << 3 == 0, "Field can only occur once"); - *fd.get() = index; + *fd.get() = ForceSync::new(jfieldid_from_field_ref(field)); fields |= 1 << 2; }, #[cfg(windows)] "handle" => unsafe { - *handle.get() = index; + *handle.get() = ForceSync::new(jfieldid_from_field_ref(field)); fields |= 1 << 1; }, "append" => unsafe { - *append.get() = index; + *append.get() = ForceSync::new(jfieldid_from_field_ref(field)); fields |= 1; }, _ => {}, diff --git a/runtime/src/native/java/io/FileInputStream.rs b/runtime/src/native/java/io/FileInputStream.rs index e2c78de..8f48ba3 100644 --- a/runtime/src/native/java/io/FileInputStream.rs +++ b/runtime/src/native/java/io/FileInputStream.rs @@ -1,20 +1,23 @@ #![allow(non_upper_case_globals)] use crate::classpath::classloader::ClassLoader; -use crate::reference::Reference; +use crate::native::jni::jfieldid_from_field_ref; +use crate::objects::reference::Reference; use std::cell::SyncUnsafeCell; -use std::ptr::NonNull; +use std::ptr::{self, NonNull}; use std::sync::atomic::{AtomicBool, Ordering}; use ::jni::env::JniEnv; -use ::jni::sys::{jboolean, jint, jlong}; +use ::jni::sys::{jboolean, jfieldID, jint, jlong}; +use common::sync::ForceSync; use symbols::sym; include_generated!("native/java/io/def/FileInputStream.definitions.rs"); /// `java.io.FileInputStream#fd` field offset -static fd: SyncUnsafeCell = SyncUnsafeCell::new(0); +static fd: SyncUnsafeCell> = + SyncUnsafeCell::new(ForceSync::new(ptr::null_mut() as _)); // throws FileNotFoundException pub fn open0(_: NonNull, _this: Reference, _name: Reference /* java.lang.String */) { @@ -81,10 +84,10 @@ pub fn initIDs(_: NonNull) { } let mut field_set = false; - for (index, field) in class.fields().enumerate() { + for field in class.fields() { if field.name == sym!(fd) { unsafe { - *fd.get() = index; + *fd.get() = ForceSync::new(jfieldid_from_field_ref(field)); } field_set = true; break; diff --git a/runtime/src/native/java/io/FileOutputStream.rs b/runtime/src/native/java/io/FileOutputStream.rs index 0c2ddff..607bf61 100644 --- a/runtime/src/native/java/io/FileOutputStream.rs +++ b/runtime/src/native/java/io/FileOutputStream.rs @@ -1,18 +1,35 @@ use crate::classpath::classloader::ClassLoader; +use crate::native::jni::{field_ref_from_jfieldid, jfieldid_from_field_ref}; use crate::native::Reference; +use crate::objects::class_instance::Instance; +use crate::thread::JavaThread; use std::cell::SyncUnsafeCell; -use std::ptr::NonNull; +use std::io::Write; +use std::os::fd::{FromRawFd, RawFd}; +use std::ptr::{self, NonNull}; use std::sync::atomic::{AtomicBool, Ordering}; use ::jni::env::JniEnv; -use ::jni::sys::{jboolean, jint}; +use ::jni::sys::{jboolean, jfieldID, jint}; +use common::sync::ForceSync; +use common::traits::PtrType; use symbols::sym; include_generated!("native/java/io/def/FileOutputStream.definitions.rs"); -/// `java.io.FileInputStream#fd` field offset -static fd: SyncUnsafeCell = SyncUnsafeCell::new(0); +/// `java.io.FileOutputStream#fd` field offset +static fd: SyncUnsafeCell> = + SyncUnsafeCell::new(ForceSync::new(ptr::null_mut() as _)); +fn get_fd(this: &Reference) -> jint { + // `fd` is a reference to a `java.io.FileDescriptor` + let fd_value = unsafe { &*fd.get() }; + let field = + unsafe { field_ref_from_jfieldid(fd_value.0) }.expect("field should always be present"); + let file_descriptor_ref = this.get_field_value(field).expect_reference(); + + super::FileDescriptor::get_fd(&file_descriptor_ref) +} // throws FileNotFoundException pub fn open0(_: NonNull, _this: Reference, _name: Reference /* java.lang.String */) { @@ -25,15 +42,58 @@ pub fn write(_: NonNull, _this: Reference, _b: jint, _append: jboolean) } // throws IOException +#[allow(trivial_numeric_casts)] pub fn writeBytes( - _: NonNull, - _this: Reference, - _b: Reference, // byte[] - _off: jint, - _len: jint, + env: NonNull, + this: Reference, + b: Reference, // byte[] + off: jint, + len: jint, _append: jboolean, ) { - unimplemented!("java.io.FileOutputStream#write"); + if b.is_null() { + let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + 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()) }; + panic!("IndexOutOfBoundsException"); // TODO + } + + if len == 0 { + return; + } + + // Need to convert the jbyte[] to a &[u8] + let window = &array_content[off as usize..(off + len) as usize]; + + // SAFETY: `i8` and `u8` have the same size and alignment + let mut window: &[u8] = + unsafe { std::slice::from_raw_parts(window.as_ptr() as *const u8, window.len()) }; + + let mut offset = 0; + let mut len = len; + while len > 0 { + let current_fd = get_fd(&this); + if current_fd == -1 { + let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + panic!("IOException, stream closed"); // TODO + } + + let mut file = 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()) }; + panic!("IOException"); // TODO + }; + + offset += n; + len -= n as i32; + window = &window[n..]; + } } pub fn initIDs(_: NonNull) { @@ -52,10 +112,10 @@ pub fn initIDs(_: NonNull) { } let mut field_set = false; - for (index, field) in class.fields().enumerate() { + for field in class.fields() { if field.name == sym!(fd) { unsafe { - *fd.get() = index; + *fd.get() = ForceSync::new(jfieldid_from_field_ref(field)); } field_set = true; break; diff --git a/runtime/src/native/java/io/UnixFileSystem.rs b/runtime/src/native/java/io/UnixFileSystem.rs new file mode 100644 index 0000000..fa8bf0e --- /dev/null +++ b/runtime/src/native/java/io/UnixFileSystem.rs @@ -0,0 +1,176 @@ +#![allow(non_upper_case_globals)] + +use crate::classpath::classloader::ClassLoader; +use crate::native::jni::jfieldid_from_field_ref; +use crate::objects::reference::Reference; + +use std::cell::SyncUnsafeCell; +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 symbols::sym; + +include_generated!("native/java/io/def/UnixFileSystem.definitions.rs"); + +/// `java.io.File#path` field offset +static path: SyncUnsafeCell> = + SyncUnsafeCell::new(ForceSync::new(ptr::null_mut() as _)); + +pub fn canonicalize0( + _: NonNull, + _this: Reference, + _path: Reference, // java.lang.String +) -> Reference { + unimplemented!("java.io.UnixFileSystem#canonicalize0"); +} + +pub fn getBooleanAttributes0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File +) -> jint { + unimplemented!("java.io.UnixFileSystem#getBooleanAttributes0"); +} + +pub fn checkAccess0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File + _access: jint, +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#checkAccess0"); +} + +pub fn getLastModifiedTime0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File +) -> jlong { + unimplemented!("java.io.UnixFileSystem#getLastModifiedTime0"); +} + +pub fn getLength0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File +) -> jlong { + unimplemented!("java.io.UnixFileSystem#getLength0"); +} + +pub fn setPermission0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File + _access: jint, + _enable: jboolean, + _owneronly: jboolean, +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#setPermission0"); +} + +pub fn createFileExclusively0( + _: NonNull, + _this: Reference, + _path: Reference, // java.lang.String +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#createFileExclusively0"); +} + +pub fn delete0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#delete0"); +} + +pub fn list0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File +) -> Reference { + unimplemented!("java.io.UnixFileSystem#list0"); +} + +pub fn createDirectory0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#createDirectory0"); +} + +pub fn rename0( + _: NonNull, + _this: Reference, + _f1: Reference, // java.io.File + _f2: Reference, // java.io.File +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#rename0"); +} + +pub fn setLastModifiedTime0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File + _time: jlong, +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#setLastModifiedTime0"); +} + +pub fn setReadOnly0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File +) -> jboolean { + unimplemented!("java.io.UnixFileSystem#setReadOnly0"); +} + +pub fn getSpace0( + _: NonNull, + _this: Reference, + _f: Reference, // java.io.File + _t: jint, +) -> jlong { + unimplemented!("java.io.UnixFileSystem#getSpace0"); +} + +pub fn getNameMax0( + _: NonNull, + _this: Reference, + _path: Reference, // java.lang.String +) -> jlong { + unimplemented!("java.io.UnixFileSystem#getNameMax0"); +} + +pub fn initIDs(_: NonNull) { + static ONCE: AtomicBool = AtomicBool::new(false); + if ONCE + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_err() + { + // TODO + panic!("java.io.UnixFileSystem#initIDs: attempt to initialize more than once."); + } + + let class = ClassLoader::lookup_class(sym!(java_io_File)).unwrap(); + unsafe { + crate::globals::classes::set_java_io_File(class); + } + + let mut field_set = false; + for field in class.fields() { + if field.name == sym!(path) { + unsafe { + *path.get() = ForceSync::new(jfieldid_from_field_ref(field)); + } + field_set = true; + break; + } + } + + assert!(field_set, "Field must be present"); +} diff --git a/runtime/src/native/java/io/def/UnixFileSystem.def b/runtime/src/native/java/io/def/UnixFileSystem.def new file mode 100644 index 0000000..7ca29d5 --- /dev/null +++ b/runtime/src/native/java/io/def/UnixFileSystem.def @@ -0,0 +1,36 @@ +import java.lang.String; +import java.io.File; + +final class UnixFileSystem extends FileSystem { + private native String canonicalize0(String path) throws IOException; + + private native int getBooleanAttributes0(File f); + + private native boolean checkAccess0(File f, int access); + + private native long getLastModifiedTime0(File f); + + private native long getLength0(File f); + + private native boolean setPermission0(File f, int access, boolean enable, boolean owneronly); + + private native boolean createFileExclusively0(String path) throws IOException; + + private native boolean delete0(File f); + + private native String[] list0(File f); + + private native boolean createDirectory0(File f); + + private native boolean rename0(File f1, File f2); + + private native boolean setLastModifiedTime0(File f, long time); + + private native boolean setReadOnly0(File f); + + private native long getSpace0(File f, int t); + + private native long getNameMax0(String path); + + private static native void initIDs(); +} \ No newline at end of file diff --git a/runtime/src/native/java/lang/Class.rs b/runtime/src/native/java/lang/Class.rs index 3b4f69a..9f0dee8 100644 --- a/runtime/src/native/java/lang/Class.rs +++ b/runtime/src/native/java/lang/Class.rs @@ -1,17 +1,18 @@ -use crate::class_instance::Instance; use crate::include_generated; +use crate::native::jni::safe_jclass_from_classref; use crate::native::JniEnv; +use crate::objects::class_instance::Instance; use crate::objects::mirror::MirrorInstance; -use crate::reference::Reference; +use crate::objects::reference::Reference; use crate::string_interner::StringInterner; +use crate::thread::JavaThread; use std::ptr::NonNull; use std::sync::Arc; -use ::jni::sys::jboolean; +use ::jni::sys::{jboolean, jint}; use common::traits::PtrType; use instructions::Operand; -use jni::sys::jint; use symbols::sym; include_generated!("native/java/lang/def/Class.registerNatives.rs"); @@ -36,14 +37,27 @@ pub fn isInstance( unimplemented!("Class#isInstance"); } pub fn isAssignableFrom( - _env: NonNull, - _this: Reference, // java.lang.Class - _cls: Reference, // java.lang.Class + env: NonNull, + this: Reference, // java.lang.Class + cls: Reference, // java.lang.Class ) -> jboolean { - unimplemented!("Class#isAssignableFrom"); + if cls.is_null() { + let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + panic!("NullPointerException"); // TODO + } + + // For clarity + let sub = cls.extract_target_class(); + let super_ = this.extract_target_class(); + + let env = unsafe { &(*env.as_ptr()) }; + env.is_assignable_from( + safe_jclass_from_classref(sub), + safe_jclass_from_classref(super_), + ) } -pub fn isInterface(_env: NonNull, _this: Reference /* java.lang.Class */) -> jboolean { - unimplemented!("Class#isInterface"); +pub fn isInterface(_env: NonNull, this: Reference /* java.lang.Class */) -> jboolean { + this.extract_target_class().is_interface() } pub fn isArray(_env: NonNull, this: Reference /* java.lang.Class */) -> jboolean { this.extract_mirror().get().is_array() @@ -57,12 +71,12 @@ pub fn initClassName( this: Reference, // java.lang.Class ) -> Reference { let this_mirror = this.extract_mirror(); - let this_mirror_target = this_mirror.get().expect_class(); // TODO: Support primitive mirrors + let this_mirror_target = this_mirror.get().target_class(); let this_name = this_mirror_target.name; let name_string = StringInterner::intern_symbol(this_name); this_mirror.get_mut().put_field_value0( - crate::globals::field_offsets::class_name_field_offset(), + crate::globals::field_offsets::java_lang_Class::name_field_offset(), Operand::Reference(Reference::class(Arc::clone(&name_string))), ); @@ -214,7 +228,7 @@ pub fn desiredAssertionStatus0( clazz: Reference, // java/lang/Class ) -> jboolean { let mirror = clazz.extract_mirror(); - let _name = &mirror.get().expect_class().name; + let _name = &mirror.get().target_class().name; false } diff --git a/runtime/src/native/java/lang/ClassLoader.rs b/runtime/src/native/java/lang/ClassLoader.rs index 7d16590..a37f6dd 100644 --- a/runtime/src/native/java/lang/ClassLoader.rs +++ b/runtime/src/native/java/lang/ClassLoader.rs @@ -1,5 +1,5 @@ use crate::native::JniEnv; -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::ptr::NonNull; diff --git a/runtime/src/native/java/lang/Object.rs b/runtime/src/native/java/lang/Object.rs index c5ea54e..ca31377 100644 --- a/runtime/src/native/java/lang/Object.rs +++ b/runtime/src/native/java/lang/Object.rs @@ -1,7 +1,7 @@ -use crate::class_instance::{ArrayInstancePtr, ClassInstancePtr}; use crate::include_generated; use crate::native::JniEnv; -use crate::reference::Reference; +use crate::objects::class_instance::{ArrayInstancePtr, ClassInstancePtr}; +use crate::objects::reference::Reference; use std::ptr::NonNull; @@ -47,8 +47,8 @@ pub fn notify(_: NonNull, _this: Reference /* java.lang.Object */) { unimplemented!("Object#notify") } -pub fn notifyAll(_: NonNull, _this: Reference /* java.lang.Object */) { - unimplemented!("Object#notifyAll") +pub fn notifyAll(_: NonNull, this: Reference /* java.lang.Object */) { + this.notify_all(); } pub fn wait0( diff --git a/runtime/src/native/java/lang/Runtime.rs b/runtime/src/native/java/lang/Runtime.rs index 0e0527f..bf6f997 100644 --- a/runtime/src/native/java/lang/Runtime.rs +++ b/runtime/src/native/java/lang/Runtime.rs @@ -1,4 +1,4 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::ptr::NonNull; diff --git a/runtime/src/native/java/lang/System.rs b/runtime/src/native/java/lang/System.rs index 31dd5e6..fe4e5d7 100644 --- a/runtime/src/native/java/lang/System.rs +++ b/runtime/src/native/java/lang/System.rs @@ -1,5 +1,6 @@ use crate::classpath::classloader::ClassLoader; -use crate::reference::Reference; +use crate::objects::reference::Reference; +use crate::thread::JavaThread; use std::ptr::NonNull; use std::time::{SystemTime, UNIX_EPOCH}; @@ -52,7 +53,7 @@ pub fn nanoTime(_env: NonNull) -> jlong { } pub fn arraycopy( - _env: NonNull, + env: NonNull, src: Reference, // java.lang.Object src_pos: jint, dest: Reference, // java.lang.Object @@ -60,8 +61,8 @@ pub fn arraycopy( length: jint, ) { if src.is_null() || dest.is_null() { - // TODO - panic!("NullPointerException") + let _thread = unsafe { &*JavaThread::for_env(env.as_ptr()) }; + todo!("NullPointerException") } let src_array = src.extract_array(); @@ -89,8 +90,8 @@ pub fn arraycopy( ); } -pub fn identityHashCode(_env: NonNull, _x: Reference /* java.lang.Object */) -> jlong { - unimplemented!("System#identityHashCode") +pub fn identityHashCode(env: NonNull, x: Reference /* java.lang.Object */) -> jint { + crate::native::java::lang::Object::hashCode(env, x) } pub fn mapLibraryName(_env: NonNull, _libname: Reference) -> Reference { diff --git a/runtime/src/native/java/lang/Thread.rs b/runtime/src/native/java/lang/Thread.rs index 6406678..458bf4f 100644 --- a/runtime/src/native/java/lang/Thread.rs +++ b/runtime/src/native/java/lang/Thread.rs @@ -1,14 +1,13 @@ -use crate::class_instance::Instance; -use crate::reference::Reference; -use crate::thread::java_lang_Thread; -use crate::JavaThread; +use crate::objects::reference::Reference; +use crate::thread::pool::ThreadPool; +use crate::thread::{java_lang_Thread, JavaThread, JavaThreadBuilder, ThreadStatus}; +use std::cmp; use std::ptr::NonNull; use std::sync::atomic::AtomicUsize; use ::jni::env::JniEnv; use ::jni::sys::{jboolean, jint, jlong}; -use common::traits::PtrType; include_generated!("native/java/lang/def/Thread.registerNatives.rs"); include_generated!("native/java/lang/def/Thread.definitions.rs"); @@ -60,8 +59,30 @@ pub fn sleepNanos0(_env: NonNull, _nanos: jlong) { unimplemented!("java.lang.Thread#sleepNanos0"); } -pub fn start0(_env: NonNull, _this: Reference /* java.lang.Thread */) { - unimplemented!("java.lang.Thread#start0"); +pub fn start0(_env: NonNull, this: Reference /* java.lang.Thread */) { + { + let existing_thread = unsafe { ThreadPool::find_from_obj(this.clone()) }; + if existing_thread.is_some() { + JavaThread::current() + .throw_exception(todo!("Throw java.lang.IllegalThreadStateException")) + } + } + + let stack_size_raw = java_lang_Thread::holder::stack_size(&this); + + let mut thread_builder = JavaThreadBuilder::new() + .obj(this) + .entry_point(JavaThread::default_entry_point); + + if stack_size_raw > 0 { + let stack_size = cmp::min(stack_size_raw as usize, u32::MAX as usize); + thread_builder = thread_builder.stack_size(stack_size); + } + + let thread = thread_builder.finish(); + + let obj = thread.obj().expect("current thread object should exist"); + java_lang_Thread::holder::set_thread_status(obj, ThreadStatus::Runnable); } pub fn holdsLock(_env: NonNull, _obj: Reference /* java.lang.Object */) -> jboolean { @@ -93,9 +114,9 @@ pub fn setPriority0( this: Reference, // java.lang.Thread new_priority: jint, ) { - java_lang_Thread::set_priority(this.clone(), new_priority); + java_lang_Thread::holder::set_priority(this.clone(), new_priority); - let java_thread = unsafe { JavaThread::for_obj(this.extract_class()) }; + let java_thread = unsafe { ThreadPool::find_from_obj(this) }; let Some(thread) = java_thread else { return; }; diff --git a/runtime/src/native/java/lang/Throwable.rs b/runtime/src/native/java/lang/Throwable.rs index 2432097..bc27f45 100644 --- a/runtime/src/native/java/lang/Throwable.rs +++ b/runtime/src/native/java/lang/Throwable.rs @@ -1,7 +1,7 @@ -use crate::class_instance::{ArrayContent, ArrayInstance, Instance}; use crate::classpath::classloader::ClassLoader; -use crate::reference::Reference; -use crate::JavaThread; +use crate::objects::class_instance::{ArrayContent, ArrayInstance, Instance}; +use crate::objects::reference::Reference; +use crate::thread::JavaThread; use std::ptr::NonNull; @@ -15,11 +15,12 @@ use symbols::sym; #[allow(non_upper_case_globals)] mod stacktrace_element { - use crate::class::Class; - use crate::class_instance::{ClassInstance, Instance}; - use crate::frame::Frame; - use crate::reference::Reference; + use crate::objects::class::Class; + use crate::objects::class_instance::{ClassInstance, Instance}; + use crate::objects::constant_pool::cp_types; + use crate::objects::reference::Reference; use crate::string_interner::StringInterner; + use crate::thread::frame::stack::VisibleStackFrame; use std::sync::atomic::{AtomicBool, Ordering}; @@ -103,13 +104,16 @@ mod stacktrace_element { ); } - pub fn from_stack_frame(stacktrace_element_class: &'static Class, frame: &Frame) -> Reference { + pub fn from_stack_frame( + stacktrace_element_class: &'static Class, + frame: VisibleStackFrame<'_>, + ) -> Reference { unsafe { initialize(stacktrace_element_class); } let method = frame.method(); - let method_class = method.class.unwrap_class_instance(); + let method_class = method.class().unwrap_class_instance(); unsafe { let stacktrace_element = ClassInstance::new(stacktrace_element_class); @@ -117,7 +121,7 @@ mod stacktrace_element { // TODO: classLoaderName // TODO: moduleName // TODO: moduleVersion - let declaring_class = StringInterner::intern_symbol(method.class.name); + let declaring_class = StringInterner::intern_symbol(method.class().name); stacktrace_element.get_mut().put_field_value0( StackTraceElement_declaringClass_FIELD_OFFSET, Operand::Reference(Reference::class(declaring_class)), @@ -131,8 +135,10 @@ mod stacktrace_element { match method_class.source_file_index { Some(idx) => { - let file_name = StringInterner::intern_bytes( - method_class.constant_pool.get_constant_utf8(idx), + let file_name = StringInterner::intern_symbol( + method_class + .constant_pool + .get::(idx), ); stacktrace_element.get_mut().put_field_value0( StackTraceElement_fileName_FIELD_OFFSET, @@ -147,12 +153,14 @@ mod stacktrace_element { }, } - let pc = frame.thread().pc.load(Ordering::Relaxed) - 1; - let line_number = method.get_line_number(pc); - stacktrace_element.get_mut().put_field_value0( - StackTraceElement_lineNumber_FIELD_OFFSET, - Operand::Int(line_number), - ); + if let VisibleStackFrame::Regular(frame) = frame { + let pc = frame.thread().pc.load(Ordering::Relaxed) - 1; + let line_number = method.get_line_number(pc); + stacktrace_element.get_mut().put_field_value0( + StackTraceElement_lineNumber_FIELD_OFFSET, + Operand::Int(line_number), + ); + } Reference::class(stacktrace_element) } diff --git a/runtime/src/native/java/lang/ref/Finalizer.rs b/runtime/src/native/java/lang/ref/Finalizer.rs index 2060642..a126871 100644 --- a/runtime/src/native/java/lang/ref/Finalizer.rs +++ b/runtime/src/native/java/lang/ref/Finalizer.rs @@ -1,4 +1,4 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::ptr::NonNull; diff --git a/runtime/src/native/java/security/AccessController.rs b/runtime/src/native/java/security/AccessController.rs index 7591741..76b2c81 100644 --- a/runtime/src/native/java/security/AccessController.rs +++ b/runtime/src/native/java/security/AccessController.rs @@ -1,8 +1,9 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; -use jni::env::JniEnv; use std::ptr::NonNull; +use jni::env::JniEnv; + include_generated!("native/java/security/def/AccessController.definitions.rs"); pub fn getProtectionDomain( diff --git a/runtime/src/native/jdk/internal/loader/BootLoader.rs b/runtime/src/native/jdk/internal/loader/BootLoader.rs new file mode 100644 index 0000000..60e6253 --- /dev/null +++ b/runtime/src/native/jdk/internal/loader/BootLoader.rs @@ -0,0 +1,60 @@ +use crate::objects::class_instance::Instance; +use crate::objects::reference::Reference; +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) -> Reference /* String[] */ { + unimplemented!("jdk.internal.loader.BootLoader#getSystemPackageNames") +} + +/// Returns the location of the package of the given name, if defined by the boot loader; +/// otherwise `null` is returned. +/// +/// 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, + _name: Reference, // java.lang.String +) -> Reference /* java.lang.String */ { + unimplemented!("jdk.internal.loader.BootLoader#getSystemPackageLocation") +} + +pub fn setBootLoaderUnnamedModule0( + env: NonNull, + module: Reference, // java.lang.Module +) { + if module.is_null() { + let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + panic!("NullPointerException"); // TODO + } + + if !module.is_instance_of(crate::globals::classes::java_lang_Module()) { + let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + panic!("IllegalArgumentException"); // TODO + } + + let name = module + .get_field_value0(crate::globals::field_offsets::java_lang_Module::name_field_offset()) + .expect_reference(); + if !name.is_null() { + let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + panic!("IllegalArgumentException"); // TODO + } + + let loader = module + .get_field_value0(crate::globals::field_offsets::java_lang_Module::loader_field_offset()) + .expect_reference(); + if !loader.is_null() { + let _thread = unsafe { JavaThread::for_env(env.as_ptr()) }; + panic!("IllegalArgumentException"); // TODO + } + + tracing::warn!("(!!!) UNIMPLEMENTED jdk.internal.loader.BootLoader#setBootLoaderUnnamedModule0") +} diff --git a/runtime/src/native/jdk/internal/loader/NativeLibraries.rs b/runtime/src/native/jdk/internal/loader/NativeLibraries.rs index d112887..f20c7f8 100644 --- a/runtime/src/native/jdk/internal/loader/NativeLibraries.rs +++ b/runtime/src/native/jdk/internal/loader/NativeLibraries.rs @@ -1,4 +1,4 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::ptr::NonNull; diff --git a/runtime/src/native/jdk/internal/loader/def/BootLoader.def b/runtime/src/native/jdk/internal/loader/def/BootLoader.def new file mode 100644 index 0000000..4276eca --- /dev/null +++ b/runtime/src/native/jdk/internal/loader/def/BootLoader.def @@ -0,0 +1,17 @@ +import java.lang.String; +import java.lang.Module; + +public class BootLoader { + // Returns an array of the binary name of the packages defined by + // the boot loader, in VM internal form (forward slashes instead of dot). + private static native String[] getSystemPackageNames(); + + // Returns the location of the package of the given name, if + // defined by the boot loader; otherwise {@code null} is returned. + // + // 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). + private static native String getSystemPackageLocation(String name); + private static native void setBootLoaderUnnamedModule0(Module module); +} \ No newline at end of file diff --git a/runtime/src/native/jdk/internal/misc/CDS.rs b/runtime/src/native/jdk/internal/misc/CDS.rs index a5173f9..9a56470 100644 --- a/runtime/src/native/jdk/internal/misc/CDS.rs +++ b/runtime/src/native/jdk/internal/misc/CDS.rs @@ -1,4 +1,4 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::ptr::NonNull; diff --git a/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs b/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs index ebac9eb..63fbbe7 100644 --- a/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs +++ b/runtime/src/native/jdk/internal/misc/ScopedMemoryAccess.rs @@ -1,4 +1,4 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::ptr::NonNull; diff --git a/runtime/src/native/jdk/internal/misc/Unsafe.rs b/runtime/src/native/jdk/internal/misc/Unsafe.rs index 3b46772..346134e 100644 --- a/runtime/src/native/jdk/internal/misc/Unsafe.rs +++ b/runtime/src/native/jdk/internal/misc/Unsafe.rs @@ -1,7 +1,7 @@ -use crate::class_instance::{ArrayInstance, Instance}; use crate::native::JniEnv; use crate::objects::class::ClassInitializationState; -use crate::reference::Reference; +use crate::objects::class_instance::{ArrayInstance, Instance}; +use crate::objects::reference::Reference; use crate::string_interner::StringInterner; use crate::thread::JavaThread; @@ -100,7 +100,7 @@ where let offset = self.offset; let ptr = offset as *const T; let atomic_ptr: &A = unsafe { &*ptr.cast() }; - unsafe { atomic_ptr.load(Ordering::Acquire) } + atomic_ptr.load(Ordering::Acquire) } #[doc(hidden)] @@ -110,7 +110,12 @@ where #[doc(hidden)] unsafe fn __get_field_volatile(&self) -> T { - unimplemented!("Volatile field access") + let offset = self.offset; + let instance = self.object.extract_class(); + unsafe { + let field_value = instance.get_mut().get_field_value_raw(offset).as_ptr(); + ::get_field_volatile_impl(field_value) + } } unsafe fn put(&self, value: T) { @@ -187,6 +192,7 @@ trait UnsafeOpImpl: Sized { unsafe fn get_array_impl(array: &mut ArrayInstance, offset: usize) -> Self::Output; unsafe fn put_array_impl(array: &mut ArrayInstance, offset: usize, value: Self::Output); unsafe fn get_field_impl(field_value: *mut Operand) -> Self::Output; + unsafe fn get_field_volatile_impl(field_value: *mut Operand) -> Self::Output; unsafe fn put_field_impl(field_value: *mut Operand, value: Self::Output); } @@ -210,6 +216,13 @@ impl UnsafeOpImpl for jboolean { unsafe { (*field_value).expect_int() != 0 } } + unsafe fn get_field_volatile_impl(field_value: *mut Operand) -> Self::Output { + let field_value_ptr = + unsafe { core::intrinsics::atomic_load_acquire(&raw const field_value) }; + let field_value = unsafe { &*field_value_ptr }; + field_value.expect_int() != 0 + } + #[allow(dropping_copy_types)] unsafe fn put_field_impl(field_value: *mut Operand, value: Self::Output) { let old = unsafe { field_value.replace(Operand::from(value)) }; @@ -251,6 +264,13 @@ macro_rules! unsafe_ops { } } + #[allow(trivial_numeric_casts)] + unsafe fn get_field_volatile_impl(field_value: *mut Operand) -> Self::Output { + let field_value_ptr = unsafe { core::intrinsics::atomic_load_acquire(&raw const field_value) }; + let field_value = unsafe { &*field_value_ptr }; + field_value.[]() as [] + } + #[allow(dropping_copy_types)] unsafe fn put_field_impl(field_value: *mut Operand, value: Self::Output) { let old = unsafe { field_value.replace(Operand::from(value)) }; @@ -666,7 +686,7 @@ pub fn objectFieldOffset1( let class = class.extract_mirror(); let name_str = StringInterner::rust_string_from_java_string(name.extract_class()); - let classref = class.get().expect_class(); + let classref = class.get().target_class(); let mut offset = 0; for field in classref.fields() { @@ -715,7 +735,7 @@ pub fn ensureClassInitialized0( let current_thread = unsafe { &mut *JavaThread::for_env(env.as_ptr() as _) }; let mirror = class.extract_mirror(); - let target_class = mirror.get().expect_class(); + let target_class = mirror.get().target_class(); match target_class.initialization_state() { ClassInitializationState::Uninit => target_class.initialize(current_thread), ClassInitializationState::InProgress | ClassInitializationState::Init => {}, @@ -730,7 +750,7 @@ pub fn arrayBaseOffset0( ) -> jint { let mirror = array_class.extract_mirror(); // TODO: InvalidClassException - let _array = mirror.get().expect_class().unwrap_array_instance(); + let _array = mirror.get().target_class().unwrap_array_instance(); // TODO: We don't do byte packing like Hotspot 0 @@ -742,7 +762,7 @@ pub fn arrayIndexScale0( ) -> jint { let mirror = array_class.extract_mirror(); // TODO: InvalidClassException - let _array = mirror.get().expect_class().unwrap_array_instance(); + let _array = mirror.get().target_class().unwrap_array_instance(); // TODO: We don't do byte packing like Hotspot 1 diff --git a/runtime/src/native/jdk/internal/misc/VM.rs b/runtime/src/native/jdk/internal/misc/VM.rs index 6db7996..9b5bdb4 100644 --- a/runtime/src/native/jdk/internal/misc/VM.rs +++ b/runtime/src/native/jdk/internal/misc/VM.rs @@ -1,5 +1,5 @@ use crate::include_generated; -use crate::reference::Reference; +use crate::objects::reference::Reference; use std::ptr::NonNull; diff --git a/runtime/src/native/jdk/internal/reflect/Reflection.rs b/runtime/src/native/jdk/internal/reflect/Reflection.rs index 1455478..1f60a98 100644 --- a/runtime/src/native/jdk/internal/reflect/Reflection.rs +++ b/runtime/src/native/jdk/internal/reflect/Reflection.rs @@ -1,4 +1,4 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; use crate::thread::JavaThread; use std::ptr::NonNull; @@ -11,21 +11,39 @@ include_generated!("native/jdk/internal/reflect/def/Reflection.definitions.rs"); #[expect(clippy::match_same_arms)] pub fn getCallerClass(env: NonNull) -> Reference { let current_thread = unsafe { &*JavaThread::for_env(env.as_ptr() as _) }; + + // The call stack at this point looks something like this: + // + // [0] [ @CallerSensitive public jdk.internal.reflect.Reflection.getCallerClass ] + // [1] [ @CallerSensitive API.method ] + // [.] [ (skipped intermediate frames) ] + // [n] [ caller ] for (n, frame) in current_thread.frame_stack().iter().rev().enumerate() { let method = frame.method(); - match n { - // TODO: - // https://github.com/openjdk/jdk/blob/6a44120a16d0f06b4ed9f0ebf6b0919da7070287/src/hotspot/share/prims/jvm.cpp#L742-L744 - // https://github.com/openjdk/jdk/blob/6a44120a16d0f06b4ed9f0ebf6b0919da7070287/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java#L117 - 0 => {}, - // TODO: - // https://github.com/openjdk/jdk/blob/6a44120a16d0f06b4ed9f0ebf6b0919da7070287/src/hotspot/share/prims/jvm.cpp#L748-L750 - // https://github.com/openjdk/jdk/blob/6a44120a16d0f06b4ed9f0ebf6b0919da7070287/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java#L117 - 1 => {}, - _ => { - // TODO: https://github.com/openjdk/jdk/blob/6a44120a16d0f06b4ed9f0ebf6b0919da7070287/src/hotspot/share/oops/method.cpp#L1378 - return Reference::mirror(method.class.mirror()); - }, + + // TODO: + // https://github.com/openjdk/jdk/blob/6a44120a16d0f06b4ed9f0ebf6b0919da7070287/src/hotspot/share/prims/jvm.cpp#L742-L744 + // https://github.com/openjdk/jdk/blob/6a44120a16d0f06b4ed9f0ebf6b0919da7070287/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java#L117 + if n == 0 || n == 1 { + if n == 0 { + // TODO + tracing::warn!( + "(!!!) UNIMPLEMENTED `getCallerClass` not verifying call from Reflection" + ); + } + + if !method.is_caller_sensitive() { + // TODO + panic!( + "InternalError, `getCallerClass` is not called from a @CallerSensitive method" + ); + } + + continue; + } + + if !method.is_stack_walk_ignored() { + return Reference::mirror(method.class().mirror()); } } diff --git a/runtime/src/native/jdk/internal/util/SystemProps.rs b/runtime/src/native/jdk/internal/util/SystemProps.rs index 5aca7a0..87fa8e8 100644 --- a/runtime/src/native/jdk/internal/util/SystemProps.rs +++ b/runtime/src/native/jdk/internal/util/SystemProps.rs @@ -1,8 +1,8 @@ pub mod Raw { - use crate::class_instance::ArrayInstance; use crate::classpath::classloader::ClassLoader; use crate::include_generated; - use crate::reference::Reference; + use crate::objects::class_instance::ArrayInstance; + use crate::objects::reference::Reference; use crate::string_interner::StringInterner; use std::ptr::NonNull; diff --git a/runtime/src/native/jni/class.rs b/runtime/src/native/jni/class.rs index 69deef9..fc0ac50 100644 --- a/runtime/src/native/jni/class.rs +++ b/runtime/src/native/jni/class.rs @@ -1,14 +1,15 @@ use super::{classref_from_jclass, jclass_from_classref}; use crate::classpath::classloader::ClassLoader; +use crate::objects::class::Class; use core::ffi::{c_char, CStr}; -use crate::class::Class; -use jni::sys::{jboolean, jbyte, jclass, jobject, jsize, JNIEnv}; +use ::jni::sys::{jboolean, jbyte, jclass, jobject, jsize, JNIEnv}; +use common::traits::PtrType; use symbols::Symbol; #[no_mangle] -pub extern "system" fn DefineClass( +pub unsafe extern "system" fn DefineClass( env: *mut JNIEnv, name: *const c_char, loader: jobject, @@ -31,7 +32,7 @@ pub unsafe extern "system" fn FindClass(env: *mut JNIEnv, name: *const c_char) - #[no_mangle] pub unsafe extern "system" fn GetSuperclass(env: *mut JNIEnv, sub: jclass) -> jclass { - if let Some(class) = classref_from_jclass(sub) { + if let Some(class) = unsafe { classref_from_jclass(sub) } { if let Some(super_class) = class.super_class { return jclass_from_classref(super_class); } @@ -41,6 +42,21 @@ pub unsafe extern "system" fn GetSuperclass(env: *mut JNIEnv, sub: jclass) -> jc } #[no_mangle] -pub extern "system" fn IsAssignableFrom(env: *mut JNIEnv, sub: jclass, sup: jclass) -> jboolean { - unimplemented!("jni::IsAssignableFrom") +pub unsafe extern "system" fn IsAssignableFrom( + env: *mut JNIEnv, + sub: jclass, + sup: jclass, +) -> jboolean { + let sub = unsafe { classref_from_jclass(sub) }; + let sup = unsafe { classref_from_jclass(sup) }; + + let (Some(sub), Some(sup)) = (sub, sup) else { + panic!("Invalid arguments to `IsAssignableFrom`"); + }; + + if sub.mirror().get().is_primitive() && sup.mirror().get().is_primitive() { + return sub == sup; + } + + return sub.is_subclass_of(sup); } diff --git a/runtime/src/native/jni/invocation_api/mod.rs b/runtime/src/native/jni/invocation_api/mod.rs index 3915bd1..a00fe8f 100644 --- a/runtime/src/native/jni/invocation_api/mod.rs +++ b/runtime/src/native/jni/invocation_api/mod.rs @@ -2,14 +2,17 @@ //! //! Vendors can deliver Java-enabled applications without having to link with the Java VM source code. -use crate::{initialization, JavaThread}; +use crate::initialization; +use crate::thread::{JavaThread, JavaThreadBuilder}; + use core::ffi::c_void; +use std::ffi::CStr; + use jni::java_vm::JavaVm; use jni::sys::{ jint, jsize, JNIInvokeInterface_, JNINativeInterface_, JavaVM, JNI_EVERSION, JNI_OK, }; use jni::version::JniVersion; -use std::ffi::CStr; pub mod library; @@ -116,7 +119,7 @@ fn attach_current_thread_impl( group = Some(todo!("crate::globals::threads::main_thread_group()")); } - let thread = JavaThread::new(); + let thread = JavaThreadBuilder::new().finish(); todo!() } diff --git a/runtime/src/native/jni/mod.rs b/runtime/src/native/jni/mod.rs index 9fec19a..1420b66 100644 --- a/runtime/src/native/jni/mod.rs +++ b/runtime/src/native/jni/mod.rs @@ -4,8 +4,10 @@ #![allow(unused_variables, non_snake_case)] use crate::objects::class::Class; +use crate::objects::field::Field; -use jni::sys::jclass; +use jni::objects::{JClass, JFieldId}; +use jni::sys::{jclass, jfieldID}; pub mod array; pub mod class; @@ -24,7 +26,43 @@ pub mod version; pub mod vm; pub mod weak; -/// Create a `jclass` from a `ClassRef` +/// Create a `JFieldId` from a `Field` +#[allow(trivial_casts)] +pub fn safe_jfieldid_from_field_ref(field: &'static Field) -> JFieldId { + let raw = jfieldid_from_field_ref(field); + + // SAFETY: We know that the `jclass` is valid because it was created from a `Class` + unsafe { JFieldId::from_raw(raw) } +} + +/// Create a `jfieldID` from a `Field` +#[allow(trivial_casts)] +pub fn jfieldid_from_field_ref(field: &'static Field) -> jfieldID { + field as *const _ as jfieldID +} + +/// Create a `Field` from a `jfieldID` +pub unsafe fn field_ref_from_jfieldid(field: jfieldID) -> Option<&'static Field> { + if field.is_null() { + return None; + } + + unsafe { + let field_ptr = core::mem::transmute::(field); + Some(&*field_ptr) + } +} + +/// Create a `JClass` from a `Class` +#[allow(trivial_casts)] +pub fn safe_jclass_from_classref(class: &'static Class) -> JClass { + let raw = jclass_from_classref(class); + + // SAFETY: We know that the `jclass` is valid because it was created from a `Class` + unsafe { JClass::from_raw(raw) } +} + +/// Create a `jclass` from a `Class` #[allow(trivial_casts)] pub fn jclass_from_classref(class: &'static Class) -> jclass { class as *const _ as jclass diff --git a/runtime/src/native/lookup.rs b/runtime/src/native/lookup.rs index df6358b..b0d264a 100644 --- a/runtime/src/native/lookup.rs +++ b/runtime/src/native/lookup.rs @@ -1,7 +1,7 @@ use crate::classpath::classloader::ClassLoader; use crate::java_call; -use crate::method::Method; -use crate::reference::Reference; +use crate::objects::method::Method; +use crate::objects::reference::Reference; use crate::string_interner::StringInterner; use crate::thread::JavaThread; @@ -95,7 +95,7 @@ impl<'a> NativeNameConverter<'a> { // Start with the prefix let mut name = String::from("Java_"); - let class_name = self.method.class.name.as_str(); + let class_name = self.method.class().name.as_str(); if !Self::map_escaped_name_on(&mut name, class_name) { return None; } @@ -202,7 +202,7 @@ fn lookup_style( include_long: bool, os_style: bool, ) -> Option<*const c_void> { - let class_loader = method.class.loader; + let class_loader = method.class().loader; if class_loader == ClassLoader::Bootstrap { if let Some(entry) = crate::native::lookup_method_opt(method) { return Some(entry as _); diff --git a/runtime/src/native/mod.rs b/runtime/src/native/mod.rs index e23ad05..d71d0b7 100644 --- a/runtime/src/native/mod.rs +++ b/runtime/src/native/mod.rs @@ -4,8 +4,8 @@ pub mod intrinsics; pub mod jni; pub mod lookup; -use crate::method::Method; -use crate::reference::Reference; +use crate::objects::method::Method; +use crate::objects::reference::Reference; use crate::stack::local_stack::LocalStack; use std::collections::HashMap; @@ -65,7 +65,7 @@ pub fn lookup_method(method: &Method) -> NativeMethodPtr { /// Lookup the native method defintion for `method`, or return `None` pub fn lookup_method_opt(method: &Method) -> Option { let native_method = NativeMethodDef { - class: method.class.name, + class: method.class().name, name: method.name, descriptor: method.descriptor, }; @@ -98,6 +98,7 @@ pub(crate) mod jdk { } pub(crate) mod loader { pub(crate) mod NativeLibraries; + pub(crate) mod BootLoader; } pub(crate) mod reflect { pub(crate) mod Reflection; @@ -108,6 +109,7 @@ pub(crate) mod jdk { pub(crate) mod java { pub(crate) mod io { pub(crate) mod FileInputStream; + pub(crate) mod UnixFileSystem; pub(crate) mod FileDescriptor; pub(crate) mod FileOutputStream; } diff --git a/runtime/src/objects/class/mod.rs b/runtime/src/objects/class/mod.rs index 479f85b..95c3d1d 100644 --- a/runtime/src/objects/class/mod.rs +++ b/runtime/src/objects/class/mod.rs @@ -1,13 +1,15 @@ mod spec; pub use spec::ClassInitializationState; +use super::constant_pool::ConstantPool; use super::field::Field; use super::method::Method; use super::mirror::MirrorInstance; use super::vtable::VTable; use crate::classpath::classloader::ClassLoader; use crate::globals::PRIMITIVES; -use crate::reference::{MirrorInstanceRef, Reference}; +use crate::objects::constant_pool::cp_types; +use crate::objects::reference::{MirrorInstanceRef, Reference}; use crate::thread::JavaThread; use spec::InitializationLock; @@ -18,7 +20,7 @@ use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::Arc; use classfile::accessflags::ClassAccessFlags; -use classfile::{ClassFile, ConstantPool, ConstantPoolRef, FieldType, MethodInfo}; +use classfile::{ClassFile, FieldType, MethodInfo}; use common::box_slice; use common::int_types::{u1, u2, u4}; use instructions::Operand; @@ -119,7 +121,7 @@ pub struct Class { field_container: FieldContainer, vtable: UnsafeCell>>, - pub(super) class_ty: ClassType, + class_ty: UnsafeCell>, init_lock: Arc, @@ -150,16 +152,15 @@ impl PartialEq for Class { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum ClassType { Instance(ClassDescriptor), Array(ArrayDescriptor), } -#[derive(Clone)] pub struct ClassDescriptor { pub source_file_index: Option, - pub constant_pool: ConstantPoolRef, + pub constant_pool: ConstantPool, } impl Debug for ClassDescriptor { @@ -167,9 +168,10 @@ impl Debug for ClassDescriptor { let mut debug_struct = f.debug_struct("ClassDescriptor"); match self.source_file_index { - Some(idx) => debug_struct.field("source_file", &unsafe { - std::str::from_utf8_unchecked(&self.constant_pool.get_constant_utf8(idx)) - }), + Some(idx) => debug_struct.field( + "source_file", + &self.constant_pool.get::(idx), + ), None => debug_struct.field("source_file", &"None"), }; @@ -185,12 +187,18 @@ pub struct ArrayDescriptor { // Getters impl Class { + fn class_ty(&self) -> &'static ClassType { + // SAFETY: The only way to construct a `Class` is via `Class::new()`, which ensures that the + // class type is initialized. + unsafe { (&*self.class_ty.get()).assume_init_ref() } + } + /// Get a reference to the constant pool for this class /// /// This returns an `Option`, as array classes do not have an associated constant pool. It is /// guaranteed to be present otherwise. - pub fn constant_pool(&self) -> Option<&ConstantPool> { - match &self.class_ty { + pub fn constant_pool(&self) -> Option<&'static ConstantPool> { + match self.class_ty() { ClassType::Instance(instance) => Some(&instance.constant_pool), _ => None, } @@ -371,7 +379,7 @@ impl Class { /// Whether the class represents an array pub fn is_array(&self) -> bool { - matches!(self.class_ty, ClassType::Array(_)) + matches!(self.class_ty(), ClassType::Array(_)) } /// Whether the class is an interface @@ -501,12 +509,6 @@ impl Class { let static_field_slots = box_slice![UnsafeCell::new(Operand::Empty); static_field_count]; - // We need the Class instance to create our methods and fields - let class_instance = ClassDescriptor { - source_file_index, - constant_pool, - }; - let class = Self { name, access_flags, @@ -517,13 +519,25 @@ impl Class { mirror: UnsafeCell::new(MaybeUninit::uninit()), // Set later field_container: FieldContainer::new(static_field_slots), vtable: UnsafeCell::new(MaybeUninit::uninit()), // Set later - class_ty: ClassType::Instance(class_instance), + class_ty: UnsafeCell::new(MaybeUninit::uninit()), // Set later init_lock: Arc::new(InitializationLock::new()), is_initialized: Cell::new(false), }; let class: &'static mut Class = Box::leak(Box::new(class)); + // TODO: Improve? + // CIRCULAR DEPENDENCY! + // + // The `ClassDescriptor` holds a `ConstantPool`, which holds a reference to the `Class`. + let class_instance = ClassDescriptor { + source_file_index, + constant_pool: ConstantPool::new(class, constant_pool), + }; + unsafe { + *class.class_ty.get() = MaybeUninit::new(ClassType::Instance(class_instance)); + } + // Create our vtable... let vtable = new_vtable(Some(&parsed_file.methods), class); unsafe { @@ -611,7 +625,7 @@ impl Class { mirror: UnsafeCell::new(MaybeUninit::uninit()), // Set later field_container: FieldContainer::null(), vtable: UnsafeCell::new(MaybeUninit::uninit()), // Set later - class_ty: ClassType::Array(array_instance), + class_ty: UnsafeCell::new(MaybeUninit::new(ClassType::Array(array_instance))), init_lock: Arc::new(InitializationLock::new()), is_initialized: Cell::new(false), }; @@ -657,7 +671,7 @@ impl Class { /// This is only safe to call *before* the class is in use. It should never be used outside of /// class loading. pub unsafe fn set_mirror(&'static self, mirror_class: &'static Class) { - let mirror = match self.class_ty { + let mirror = match self.class_ty() { ClassType::Instance(_) => MirrorInstance::new(mirror_class, self), ClassType::Array(_) => MirrorInstance::new_array(mirror_class, self), }; @@ -718,29 +732,15 @@ impl Class { } pub fn unwrap_class_instance(&self) -> &ClassDescriptor { - match self.class_ty { - ClassType::Instance(ref instance) => instance, - _ => unreachable!(), - } - } - - pub fn unwrap_class_instance_mut(&mut self) -> &mut ClassDescriptor { - match self.class_ty { - ClassType::Instance(ref mut instance) => instance, + match self.class_ty() { + ClassType::Instance(instance) => instance, _ => unreachable!(), } } pub fn unwrap_array_instance(&self) -> &ArrayDescriptor { - match self.class_ty { - ClassType::Array(ref instance) => instance, - _ => unreachable!(), - } - } - - pub fn unwrap_array_instance_mut(&mut self) -> &mut ArrayDescriptor { - match self.class_ty { - ClassType::Array(ref mut instance) => instance, + match self.class_ty() { + ClassType::Array(instance) => instance, _ => unreachable!(), } } diff --git a/runtime/src/objects/class/spec.rs b/runtime/src/objects/class/spec.rs index f6d24f6..15b2402 100644 --- a/runtime/src/objects/class/spec.rs +++ b/runtime/src/objects/class/spec.rs @@ -1,9 +1,10 @@ -use crate::class::Class; use crate::java_call; -use crate::method::Method; use crate::method_invoker::MethodInvoker; +use crate::objects::class::Class; +use crate::objects::constant_pool::{cp_types, ConstantPool}; use crate::objects::field::Field; -use crate::reference::Reference; +use crate::objects::method::Method; +use crate::objects::reference::Reference; use crate::string_interner::StringInterner; use crate::thread::JavaThread; @@ -11,7 +12,7 @@ use std::cell::UnsafeCell; use std::sync::{Arc, Condvar, Mutex, MutexGuard}; use classfile::accessflags::MethodAccessFlags; -use classfile::{ConstantPoolRef, FieldType}; +use classfile::FieldType; use common::int_types::u2; use instructions::Operand; use symbols::{sym, Symbol}; @@ -140,20 +141,8 @@ impl Class { // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-5.html#jvms-5.4.3.2 #[tracing::instrument(skip_all)] - pub fn resolve_field( - &self, - constant_pool: ConstantPoolRef, - field_ref_idx: u2, - ) -> Option<&'static Field> { - // TODO: Double constant pool lookup - let (_, name_and_type_index) = constant_pool.get_field_ref(field_ref_idx); - - let (name_index, descriptor_index) = constant_pool.get_name_and_type(name_and_type_index); - - let field_name = constant_pool.get_constant_utf8(name_index); - let mut descriptor = constant_pool.get_constant_utf8(descriptor_index); - - let field_type = FieldType::parse(&mut descriptor).unwrap(); // TODO: Error handling + pub fn resolve_field(&self, name: Symbol, descriptor: Symbol) -> Option<&'static Field> { + let field_type = FieldType::parse(&mut descriptor.as_bytes()).unwrap(); // TODO: Error handling // When resolving a field reference, field resolution first attempts to look up // the referenced field in C and its superclasses: @@ -161,7 +150,7 @@ impl Class { // 1. If C declares a field with the name and descriptor specified by the field reference, // field lookup succeeds. The declared field is the result of the field lookup. for field in self.fields() { - if field.name.as_bytes() == field_name && field.descriptor == field_type { + if field.name == name && field.descriptor == field_type { return Some(field); } } @@ -172,7 +161,7 @@ impl Class { // 3. Otherwise, if C has a superclass S, field lookup is applied recursively to S. if let Some(super_class) = &self.super_class { - if let Some(field) = super_class.resolve_field(constant_pool, field_ref_idx) { + if let Some(field) = super_class.resolve_field(name, descriptor) { return Some(field); } } @@ -184,30 +173,13 @@ impl Class { // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-5.html#jvms-5.4.3.3 #[tracing::instrument(skip_all)] pub fn resolve_method<'a>( - &'a self, - thread: &JavaThread, - method_ref_idx: u2, + class: &'static Class, + interface_method: bool, // TODO: Should just call `resolve_interface_method` directly + name: Symbol, + descriptor: Symbol, ) -> Option<&'static Method> { - let descriptor = self.unwrap_class_instance(); - let constant_pool = Arc::clone(&descriptor.constant_pool); - - let (interface_method, class_name_index, name_and_type_index) = - constant_pool.get_method_ref(method_ref_idx); - - let (method_name_index, method_descriptor_index) = - constant_pool.get_name_and_type(name_and_type_index); - - let method_name_raw = constant_pool.get_constant_utf8(method_name_index); - let descriptor_raw = constant_pool.get_constant_utf8(method_descriptor_index); - - let method_name = Symbol::intern_bytes(method_name_raw); - let descriptor = Symbol::intern_bytes(descriptor_raw); - - let class_name = constant_pool.get_class_name(class_name_index); - let class = self.loader.load(Symbol::intern_bytes(class_name)).unwrap(); - if interface_method { - return class.resolve_interface_method(thread, method_name, descriptor); + return class.resolve_interface_method(name, descriptor); } // When resolving a method reference: @@ -218,18 +190,23 @@ impl Class { } // 2. Otherwise, method resolution attempts to locate the referenced method in C and its superclasses: - if let ret @ Some(_) = class.resolve_method_step_two(method_name, descriptor) { + if let ret @ Some(_) = class.resolve_method_step_two(name, descriptor) { return ret; } - // TODO: Method resolution in superinterfaces // 3. Otherwise, method resolution attempts to locate the referenced method in the superinterfaces of the specified class C: // 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) { + 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) { + return Some(method); + } // 3.3. Otherwise, method lookup fails. panic!("NoSuchMethodError") // TODO @@ -277,7 +254,6 @@ impl Class { // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-5.html#jvms-5.4.3.4 fn resolve_interface_method( &self, - _thread: &JavaThread, method_name: Symbol, descriptor: Symbol, ) -> Option<&'static Method> { @@ -425,7 +401,7 @@ impl Class { | FieldType::Int => { let constant_value = class_instance .constant_pool - .get_integer(constant_value_index); + .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { self.set_static_field(field.idx, value); @@ -434,23 +410,25 @@ impl Class { FieldType::Double => { let constant_value = class_instance .constant_pool - .get_double(constant_value_index); + .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { self.set_static_field(field.idx, value); } }, FieldType::Float => { - let constant_value = - class_instance.constant_pool.get_float(constant_value_index); + let constant_value = class_instance + .constant_pool + .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { self.set_static_field(field.idx, value); } }, FieldType::Long => { - let constant_value = - class_instance.constant_pool.get_long(constant_value_index); + let constant_value = class_instance + .constant_pool + .get::(constant_value_index); let value = Operand::from(constant_value); unsafe { self.set_static_field(field.idx, value); @@ -459,8 +437,8 @@ impl Class { FieldType::Object(ref obj) if &**obj == b"java/lang/String" => { let raw_string = class_instance .constant_pool - .get_string(constant_value_index); - let string_instance = StringInterner::intern_bytes(raw_string); + .get::(constant_value_index); + let string_instance = StringInterner::intern_symbol(raw_string); let value = Operand::Reference(Reference::class(string_instance)); unsafe { self.set_static_field(field.idx, value); diff --git a/runtime/src/objects/class_instance.rs b/runtime/src/objects/class_instance.rs index 410d31e..036f312 100644 --- a/runtime/src/objects/class_instance.rs +++ b/runtime/src/objects/class_instance.rs @@ -1,7 +1,7 @@ -use crate::class::Class; use crate::classpath::classloader::ClassLoader; -use crate::field::Field; -use crate::reference::{ArrayInstanceRef, ClassInstanceRef, Reference}; +use crate::objects::class::Class; +use crate::objects::field::Field; +use crate::objects::reference::{ArrayInstanceRef, ClassInstanceRef, Reference}; use std::fmt::{Debug, Formatter}; use std::ptr::NonNull; diff --git a/runtime/src/objects/constant_pool/cp_types.rs b/runtime/src/objects/constant_pool/cp_types.rs new file mode 100644 index 0000000..1e44a3f --- /dev/null +++ b/runtime/src/objects/constant_pool/cp_types.rs @@ -0,0 +1,253 @@ +use super::entry::ResolvedEntry; +use crate::objects::class::Class as ClassObj; +use crate::objects::field::Field; +use crate::objects::method::Method; + +use common::int_types::{s4, s8, u2}; +use symbols::Symbol; + +pub enum Entry { + Class(::Resolved), + Integer(::Resolved), + Double(::Resolved), + Float(::Resolved), + Long(::Resolved), + ClassName(::Resolved), + ConstantUtf8(::Resolved), + FieldRef(::Resolved), + MethodRef(::Resolved), + String(::Resolved), + MethodHandle(u32), + MethodType(u32), +} + +/// A trait for types that can be stored in the constant pool. +pub trait EntryType: sealed::Sealed { + type Resolved; + + #[doc(hidden)] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved; + + /// Resolve the entry at the given index in the constant pool. + /// + /// # Panics + /// + /// This will panic in the event of any resolution failure. There should never be a case + /// where a *valid* constant pool entry cannot be resolved. + #[doc(hidden)] + fn resolve(class: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry; +} + +pub struct Class; + +impl EntryType for Class { + type Resolved = &'static ClassObj; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.class } + } + + fn resolve(class: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let entry = ClassName::resolve(class, cp, index); + let name = ClassName::resolved_entry(entry); + + let class = class.loader.load(name).unwrap(); + ResolvedEntry { class } + } +} + +pub struct Integer; + +impl EntryType for Integer { + type Resolved = s4; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.integer } + } + + fn resolve(_: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let integer = cp.raw().get_integer(index); + ResolvedEntry { integer } + } +} + +pub struct Double; + +impl EntryType for Double { + type Resolved = f64; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.double } + } + + fn resolve(_: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let double = cp.raw().get_double(index); + ResolvedEntry { double } + } +} + +pub struct Float; + +impl EntryType for Float { + type Resolved = f32; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.float } + } + + fn resolve(_: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let float = cp.raw().get_float(index); + ResolvedEntry { float } + } +} + +pub struct Long; + +impl EntryType for Long { + type Resolved = s8; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.long } + } + + fn resolve(_: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let long = cp.raw().get_long(index); + ResolvedEntry { long } + } +} + +pub struct ClassName; + +impl EntryType for ClassName { + type Resolved = Symbol; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.class_name } + } + + fn resolve(_: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let utf8 = cp.raw().get_class_name(index); + + let class_name = Symbol::intern_bytes(utf8); + ResolvedEntry { class_name } + } +} + +pub struct ConstantUtf8; + +impl EntryType for ConstantUtf8 { + type Resolved = Symbol; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.constant_utf8 } + } + + fn resolve(_: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let utf8_raw = cp.raw().get_constant_utf8(index); + + let utf8 = Symbol::intern_bytes(utf8_raw); + ResolvedEntry { + constant_utf8: utf8, + } + } +} + +pub struct FieldRef; + +impl EntryType for FieldRef { + type Resolved = &'static Field; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.field_ref } + } + + fn resolve(class: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let (class_index, name_and_type_index) = cp.raw().get_field_ref(index); + + let class_entry = Class::resolve(class, cp, class_index); + let class = Class::resolved_entry(class_entry); + + let (name_index, descriptor_index) = cp.raw().get_name_and_type(name_and_type_index); + + let name_entry = ConstantUtf8::resolve(class, cp, name_index); + let name = ConstantUtf8::resolved_entry(name_entry); + + let descriptor_entry = ConstantUtf8::resolve(class, cp, descriptor_index); + let descriptor = ConstantUtf8::resolved_entry(descriptor_entry); + + let field = class.resolve_field(name, descriptor).unwrap(); + ResolvedEntry { field_ref: field } + } +} + +pub struct MethodRef; + +impl EntryType for MethodRef { + type Resolved = &'static Method; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.method_ref } + } + + fn resolve(class: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let (is_interface, class_index, name_and_type_index) = cp.raw().get_method_ref(index); + + let class_entry = Class::resolve(class, cp, class_index); + let class = Class::resolved_entry(class_entry); + + let (name_index, descriptor_index) = cp.raw().get_name_and_type(name_and_type_index); + + let name_entry = ConstantUtf8::resolve(class, cp, name_index); + let name = ConstantUtf8::resolved_entry(name_entry); + + 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(); + ResolvedEntry { method_ref } + } +} + +pub struct String; + +impl EntryType for String { + type Resolved = Symbol; + + #[inline] + fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { + unsafe { entry.string } + } + + fn resolve(_: &'static ClassObj, cp: &super::ConstantPool, index: u2) -> ResolvedEntry { + let string_raw = cp.raw().get_string(index); + + let string = Symbol::intern_bytes(string_raw); + ResolvedEntry { string } + } +} + +mod sealed { + use super::*; + + pub trait Sealed {} + + impl Sealed for Class {} + impl Sealed for Integer {} + impl Sealed for Double {} + impl Sealed for Float {} + impl Sealed for Long {} + impl Sealed for ClassName {} + impl Sealed for ConstantUtf8 {} + impl Sealed for FieldRef {} + impl Sealed for MethodRef {} + impl Sealed for String {} +} diff --git a/runtime/src/objects/constant_pool/entry.rs b/runtime/src/objects/constant_pool/entry.rs new file mode 100644 index 0000000..b1678a7 --- /dev/null +++ b/runtime/src/objects/constant_pool/entry.rs @@ -0,0 +1,71 @@ +use super::cp_types; +use crate::objects::class::Class; +use crate::objects::field::Field; +use crate::objects::method::Method; + +use std::cell::UnsafeCell; +use std::sync::Mutex; + +use common::int_types::{s4, s8, u2}; +use symbols::Symbol; + +#[derive(Copy, Clone)] +pub(super) union ResolvedEntry { + pub(super) integer: s4, + pub(super) double: f64, + pub(super) float: f32, + pub(super) long: s8, + pub(super) class: &'static Class, + pub(super) class_name: Symbol, + pub(super) constant_utf8: Symbol, + pub(super) field_ref: &'static Field, + pub(super) method_ref: &'static Method, + pub(super) string: Symbol, +} + +pub(super) struct ConstantPoolEntry { + resolved: UnsafeCell>, + _resolution_lock: Mutex<()>, +} + +impl ConstantPoolEntry { + pub(super) fn new() -> Self { + Self { + resolved: UnsafeCell::new(None), + _resolution_lock: Mutex::new(()), + } + } + + pub(super) fn resolved(&self) -> Option { + match self.resolved_field() { + Some(resolved) => Some(T::resolved_entry(resolved)), + None => None, + } + } + + pub(super) fn resolve( + &self, + class: &'static Class, + cp: &super::ConstantPool, + index: u2, + ) -> T::Resolved { + let _guard = self._resolution_lock.lock().unwrap(); + + // Some other thread beat us here + if self.resolved_field().is_some() { + return self.resolved::().unwrap(); + } + + // We know that there will be a resolved entry, as `EntryType::resolve` will panic on failure + let resolved = T::resolve(class, cp, index); + unsafe { + *self.resolved.get() = Some(resolved); + } + + T::resolved_entry(resolved) + } + + fn resolved_field(&self) -> Option { + unsafe { *self.resolved.get() } + } +} diff --git a/runtime/src/objects/constant_pool/mod.rs b/runtime/src/objects/constant_pool/mod.rs new file mode 100644 index 0000000..5a7dde2 --- /dev/null +++ b/runtime/src/objects/constant_pool/mod.rs @@ -0,0 +1,86 @@ +pub mod cp_types; +mod entry; + +use crate::objects::class::Class; +use cp_types::Entry; + +use std::fmt::{Debug, Formatter}; +use std::sync::Arc; + +use classfile::ConstantPoolValueInfo; +use common::box_slice; +use common::int_types::u2; + +// https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-4.html#jvms-4.4 + +pub struct ConstantPool { + class: &'static Class, + entries: Box<[entry::ConstantPoolEntry]>, + raw: classfile::ConstantPool, +} + +impl ConstantPool { + pub fn new(class: &'static Class, cp: classfile::ConstantPool) -> Self { + Self { + class, + entries: box_slice![entry::ConstantPoolEntry::new(); cp.len()], + raw: cp, + } + } + + pub fn get(&self, index: u2) -> T::Resolved { + let entry = &self.entries[index as usize]; + if let Some(resolved) = entry.resolved::() { + return resolved; + } + + entry.resolve::(self.class, &self, index) + } + + pub fn get_any(&self, index: u2) -> Entry { + let raw = &self.raw[index as usize]; + match raw { + ConstantPoolValueInfo::Class { .. } => Entry::Class(self.get::(index)), + ConstantPoolValueInfo::Fieldref { .. } => { + Entry::FieldRef(self.get::(index)) + }, + ConstantPoolValueInfo::Methodref { .. } => { + Entry::MethodRef(self.get::(index)) + }, + ConstantPoolValueInfo::String { .. } => { + Entry::String(self.get::(index)) + }, + ConstantPoolValueInfo::Integer { .. } => { + Entry::Integer(self.get::(index)) + }, + ConstantPoolValueInfo::Float { .. } => Entry::Float(self.get::(index)), + ConstantPoolValueInfo::Long { .. } => Entry::Long(self.get::(index)), + ConstantPoolValueInfo::Double { .. } => { + Entry::Double(self.get::(index)) + }, + ConstantPoolValueInfo::Utf8 { .. } => { + Entry::ConstantUtf8(self.get::(index)) + }, + ConstantPoolValueInfo::MethodHandle { .. } + | ConstantPoolValueInfo::MethodType { .. } + | ConstantPoolValueInfo::InterfaceMethodref { .. } + | ConstantPoolValueInfo::InvokeDynamic { .. } + | ConstantPoolValueInfo::Module { .. } + | ConstantPoolValueInfo::Package { .. } => todo!(), + ConstantPoolValueInfo::NameAndType { .. } => { + unreachable!("NameAndType entries should not be resolved directly") + }, + ConstantPoolValueInfo::Unusable => unreachable!(), + } + } + + pub(super) fn raw(&self) -> &classfile::ConstantPool { + &self.raw + } +} + +impl Debug for ConstantPool { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.raw.iter()).finish() + } +} diff --git a/runtime/src/objects/field.rs b/runtime/src/objects/field.rs index e78ff6c..20c7d86 100644 --- a/runtime/src/objects/field.rs +++ b/runtime/src/objects/field.rs @@ -1,10 +1,11 @@ use super::reference::Reference; -use crate::class::Class; +use crate::objects::class::Class; +use crate::objects::constant_pool::{cp_types, ConstantPool}; use std::fmt::{Debug, Formatter}; use classfile::accessflags::FieldAccessFlags; -use classfile::{ConstantPool, FieldInfo, FieldType}; +use classfile::{FieldInfo, FieldType}; use common::int_types::u2; use instructions::Operand; use symbols::Symbol; @@ -61,13 +62,12 @@ impl Field { let access_flags = field_info.access_flags; let name_index = field_info.name_index; - let name_bytes = constant_pool.get_constant_utf8(name_index); - let name = Symbol::intern_bytes(name_bytes); + let name = constant_pool.get::(name_index); let descriptor_index = field_info.descriptor_index; - let mut descriptor_bytes = constant_pool.get_constant_utf8(descriptor_index); + let descriptor = constant_pool.get::(descriptor_index); - let descriptor = FieldType::parse(&mut descriptor_bytes).unwrap(); // TODO: Error handling + let descriptor = FieldType::parse(&mut descriptor.as_bytes()).unwrap(); // TODO: Error handling let constant_value_index = field_info .get_constant_value_attribute() .map(|constant_value| constant_value.constantvalue_index); diff --git a/runtime/src/objects/method/mod.rs b/runtime/src/objects/method/mod.rs index 694a0df..3bac488 100644 --- a/runtime/src/objects/method/mod.rs +++ b/runtime/src/objects/method/mod.rs @@ -1,27 +1,53 @@ pub mod spec; -use crate::class::Class; use crate::classpath::classloader::ClassLoader; use crate::native::NativeMethodPtr; +use crate::objects::class::Class; +use crate::objects::constant_pool::cp_types; use std::ffi::c_void; use std::fmt::{Debug, Formatter}; use std::sync::{Arc, RwLock}; use classfile::accessflags::MethodAccessFlags; -use classfile::{Code, LineNumber, MethodDescriptor, MethodInfo}; +use classfile::{Annotation, Code, LineNumber, MethodDescriptor, MethodInfo, ResolvedAnnotation}; use common::int_types::{s4, u1}; use symbols::Symbol; +#[derive(Default, PartialEq, Eq, Debug)] +struct ExtraFlags { + caller_sensitive: bool, + intrinsic: bool, +} + +impl ExtraFlags { + fn from_annotations(annotations: impl Iterator) -> Self { + const CALLER_SENSITIVE_TYPE: &str = "Ljdk/internal/reflect/CallerSensitive;"; + const INTRINSIC_CANDIDATE_TYPE: &str = "Ljdk/internal/vm/annotation/IntrinsicCandidate;"; + + let mut ret = Self::default(); + + for annotation in annotations { + match &*annotation.name { + CALLER_SENSITIVE_TYPE => ret.caller_sensitive = true, + INTRINSIC_CANDIDATE_TYPE => ret.intrinsic = true, + _ => {}, + } + } + + ret + } +} + pub struct Method { - pub class: &'static Class, + class: &'static Class, pub access_flags: MethodAccessFlags, + extra_flags: ExtraFlags, pub name: Symbol, pub descriptor: Symbol, pub parameter_count: u1, pub line_number_table: Vec, pub code: Code, - pub is_intrinsic: bool, // TODO: This can be done better native_method: RwLock<*const c_void>, } @@ -29,12 +55,12 @@ impl PartialEq for Method { fn eq(&self, other: &Self) -> bool { self.class == other.class && self.access_flags == other.access_flags + && self.extra_flags == other.extra_flags && self.name == other.name && self.descriptor == other.descriptor && self.parameter_count == other.parameter_count && self.line_number_table == other.line_number_table && self.code == other.code - && self.is_intrinsic == other.is_intrinsic } } @@ -50,17 +76,27 @@ impl Method { /// NOTE: This will leak the `Method` and return a reference. It is important that this only /// be called once per method. It should never be used outside of class loading. pub(super) fn new(class: &'static Class, method_info: &MethodInfo) -> &'static mut Self { - let constant_pool = Arc::clone(&class.unwrap_class_instance().constant_pool); + let constant_pool = class.constant_pool().unwrap(); let access_flags = method_info.access_flags; + let extra_flags; + match method_info.runtime_visible_annotations(constant_pool.raw()) { + Some(annotations) => { + extra_flags = ExtraFlags::from_annotations(annotations); + }, + None => { + extra_flags = ExtraFlags::default(); + }, + } + let name_index = method_info.name_index; - let name = constant_pool.get_constant_utf8(name_index); + let name = constant_pool.get::(name_index); let descriptor_index = method_info.descriptor_index; - let descriptor_bytes = constant_pool.get_constant_utf8(descriptor_index); + let descriptor = constant_pool.get::(descriptor_index); - let parameter_count: u1 = MethodDescriptor::parse(&mut &descriptor_bytes[..]) + let parameter_count: u1 = MethodDescriptor::parse(&mut descriptor.as_bytes()) .unwrap() // TODO: Error handling .parameters .len() @@ -72,17 +108,15 @@ impl Method { .unwrap_or_default(); let code = method_info.get_code_attribute().unwrap_or_default(); - let is_intrinsic = method_info.is_intrinsic_candidate(Arc::clone(&constant_pool)); - let method = Self { class, access_flags, - name: Symbol::intern_bytes(name), - descriptor: Symbol::intern_bytes(&descriptor_bytes), + extra_flags, + name, + descriptor, parameter_count, line_number_table, code, - is_intrinsic, native_method: RwLock::new(std::ptr::null()), }; @@ -118,14 +152,11 @@ impl Method { return Some(exception_handler.handler_pc as isize); } - let catch_type_class_name = self + let catch_type_class = self .class .unwrap_class_instance() .constant_pool - .get_class_name(exception_handler.catch_type); - let catch_type_class = ClassLoader::Bootstrap - .load(Symbol::intern_bytes(catch_type_class_name)) - .expect("catch_type should be available"); + .get::(exception_handler.catch_type); if catch_type_class == class || catch_type_class.is_subclass_of(class) { return Some(exception_handler.handler_pc as isize); @@ -135,6 +166,32 @@ impl Method { None } + pub fn native_method(&self) -> Option { + let native_method = self.native_method.read().unwrap(); + if native_method.is_null() { + return None; + } + + assert!(self.is_native()); + Some(unsafe { core::mem::transmute(*native_method) }) + } + + pub fn set_native_method(&self, func: *const c_void) { + let mut lock = self.native_method.write().unwrap(); + *lock = func; + } +} + +// Getters +impl Method { + #[inline] + pub fn class(&self) -> &'static Class { + self.class + } +} + +// Flags +impl Method { pub fn is_native(&self) -> bool { self.access_flags.is_native() } @@ -163,18 +220,19 @@ impl Method { self.class.is_interface() && (!self.is_abstract() && !self.is_public()) } - pub fn native_method(&self) -> Option { - let native_method = self.native_method.read().unwrap(); - if native_method.is_null() { - return None; - } - - assert!(self.is_native()); - Some(unsafe { core::mem::transmute(*native_method) }) + /// Whether the method has the @CallerSensitive annotation + pub fn is_caller_sensitive(&self) -> bool { + self.extra_flags.caller_sensitive } - pub fn set_native_method(&self, func: *const c_void) { - let mut lock = self.native_method.write().unwrap(); - *lock = func; + pub fn is_stack_walk_ignored(&self) -> bool { + if self + .class + .is_subclass_of(crate::globals::classes::jdk_internal_reflect_MethodAccessorImpl()) + { + return true; + } + + false } } diff --git a/runtime/src/objects/method/spec.rs b/runtime/src/objects/method/spec.rs index 71a6b5c..9bebf8c 100644 --- a/runtime/src/objects/method/spec.rs +++ b/runtime/src/objects/method/spec.rs @@ -1,4 +1,4 @@ -use crate::method::Method; +use crate::objects::method::Method; use classfile::{FieldType, MethodDescriptor}; use symbols::sym; diff --git a/runtime/src/objects/mirror.rs b/runtime/src/objects/mirror.rs index cdbb22a..dfd5874 100644 --- a/runtime/src/objects/mirror.rs +++ b/runtime/src/objects/mirror.rs @@ -1,7 +1,7 @@ -use crate::class_instance::Instance; -use crate::field::Field; use crate::objects::class::Class; -use crate::reference::{MirrorInstanceRef, Reference}; +use crate::objects::class_instance::Instance; +use crate::objects::field::Field; +use crate::objects::reference::{MirrorInstanceRef, Reference}; use std::fmt::{Debug, Formatter}; use std::ptr::NonNull; @@ -19,18 +19,34 @@ enum MirrorTarget { // TODO: Make fields private /// A mirror instance /// -/// A "mirror" is simply an instance of java.lang.Class with an associated [`ClassInstance`]. -/// It contains information about the object it's describing, as well as wrapping up the object itself. +/// A "mirror" is simply an instance of `java.lang.Class` with an associated target [`Class`]. /// -/// [`ClassInstance`]: super::class_instance::ClassInstance -#[derive(Debug, Clone, PartialEq)] +/// In the following: +/// +/// ```java +/// var c = String.class; +/// ``` +/// +/// `c` is a mirror instance, with a target of `java.lang.String`. +#[derive(Clone, PartialEq)] pub struct MirrorInstance { - pub class: &'static Class, + class: &'static Class, pub fields: Box<[Operand]>, target: MirrorTarget, } +impl Debug for MirrorInstance { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MirrorInstance") + .field("class", &self.class.name.as_str()) + .field("fields", &self.fields) + .field("target", &self.target) + .finish() + } +} + impl MirrorInstance { + // TODO: Remove the `mirror_class` parameter? It's always `java.lang.Class` pub fn new(mirror_class: &'static Class, target: &'static Class) -> MirrorInstanceRef { let fields = Self::initialize_fields(mirror_class); MirrorInstancePtr::new(Self { @@ -63,13 +79,6 @@ impl MirrorInstance { }) } - pub fn has_target(&self, class: &'static Class) -> bool { - match &self.target { - MirrorTarget::Class(target) => *target == class, - _ => false, - } - } - pub fn is_primitive(&self) -> bool { matches!(&self.target, MirrorTarget::Primitive(_)) } @@ -78,17 +87,47 @@ impl MirrorInstance { matches!(&self.target, MirrorTarget::Class(class) if class.is_array()) } - pub fn expect_class(&self) -> &'static Class { + /// The backing class of this mirror + /// + /// This is always `java.lang.Class`, and is only really useful as a marker. + /// + /// In the following: + /// + /// ```java + /// var c = String.class; + /// ``` + /// + /// `c` is an instance of `Class`, which this represents. + /// + /// To get the class that this mirror is targeting (in this case, `java.lang.String`), use [`MirrorInstance::target_class`]. + pub fn class(&self) -> &'static Class { + self.class + } + + /// The class that this mirror is targeting + /// + /// In the following: + /// + /// ```java + /// var c = String.class; + /// ``` + /// + /// `String` (`java.lang.String`) is the target class. + pub fn target_class(&self) -> &'static Class { match &self.target { MirrorTarget::Class(class) => *class, - _ => panic!("Expected mirror instance to point to class!"), - } - } - - pub fn expect_primitive(&self) -> FieldType { - match &self.target { - MirrorTarget::Primitive(primitive) => primitive.clone(), - _ => panic!("Expected mirror instance to point to primitive!"), + MirrorTarget::Primitive(field_ty) => match field_ty { + FieldType::Byte => crate::globals::classes::java_lang_Byte(), + FieldType::Char => crate::globals::classes::java_lang_Character(), + FieldType::Double => crate::globals::classes::java_lang_Double(), + FieldType::Float => crate::globals::classes::java_lang_Float(), + FieldType::Int => crate::globals::classes::java_lang_Integer(), + FieldType::Long => crate::globals::classes::java_lang_Long(), + FieldType::Short => crate::globals::classes::java_lang_Short(), + FieldType::Boolean => crate::globals::classes::java_lang_Boolean(), + FieldType::Void => crate::globals::classes::java_lang_Void(), + _ => unreachable!("only primitive types should exist within primitive mirrors"), + }, } } diff --git a/runtime/src/objects/mod.rs b/runtime/src/objects/mod.rs index ed6bfa9..add5b09 100644 --- a/runtime/src/objects/mod.rs +++ b/runtime/src/objects/mod.rs @@ -1,5 +1,6 @@ pub mod class; pub mod class_instance; +pub mod constant_pool; pub mod field; pub mod method; pub mod mirror; diff --git a/runtime/src/objects/monitor.rs b/runtime/src/objects/monitor.rs index 2ac172b..3264efd 100644 --- a/runtime/src/objects/monitor.rs +++ b/runtime/src/objects/monitor.rs @@ -1,6 +1,6 @@ -use crate::JavaThread; -use std::cell::UnsafeCell; +use crate::thread::JavaThread; +use std::cell::UnsafeCell; use std::fmt::Debug; use std::mem::MaybeUninit; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/runtime/src/objects/reference.rs b/runtime/src/objects/reference.rs index 11ca770..d4e4b94 100644 --- a/runtime/src/objects/reference.rs +++ b/runtime/src/objects/reference.rs @@ -1,9 +1,9 @@ use super::field::Field; -use crate::class::Class; -use crate::class_instance::{ArrayInstancePtr, ClassInstancePtr, Instance}; -use crate::monitor::Monitor; +use crate::objects::class::Class; +use crate::objects::class_instance::{ArrayInstancePtr, ClassInstancePtr, Instance}; use crate::objects::mirror::MirrorInstancePtr; -use crate::JavaThread; +use crate::objects::monitor::Monitor; +use crate::thread::JavaThread; use std::ffi::c_void; use std::ptr::NonNull; @@ -101,6 +101,10 @@ impl Reference { pub fn monitor_exit(&self, thread: &'static JavaThread) { self.monitor.exit(thread); } + + pub fn notify_all(&self) { + self.monitor.notify_all(); + } } impl Reference { @@ -112,7 +116,7 @@ impl Reference { match &self.instance { ReferenceInstance::Class(class_instance) => class_instance.get().class().name, ReferenceInstance::Array(array_instance) => array_instance.get().class.name, - ReferenceInstance::Mirror(mirror_instance) => mirror_instance.get().class.name, + ReferenceInstance::Mirror(mirror_instance) => mirror_instance.get().target_class().name, ReferenceInstance::Null => panic!("NullPointerException"), } } @@ -136,7 +140,7 @@ impl Reference { pub fn extract_target_class(&self) -> &'static Class { match &self.instance { ReferenceInstance::Class(class) => class.get().class(), - ReferenceInstance::Mirror(mirror) => &mirror.get().class, + ReferenceInstance::Mirror(mirror) => &mirror.get().target_class(), ReferenceInstance::Array(arr) => &arr.get().class, ReferenceInstance::Null => panic!("NullPointerException"), } diff --git a/runtime/src/objects/vtable.rs b/runtime/src/objects/vtable.rs index 8497010..d5883c7 100644 --- a/runtime/src/objects/vtable.rs +++ b/runtime/src/objects/vtable.rs @@ -1,4 +1,4 @@ -use crate::method::Method; +use crate::objects::method::Method; use classfile::accessflags::MethodAccessFlags; use symbols::Symbol; diff --git a/runtime/src/stack/local_stack.rs b/runtime/src/stack/local_stack.rs index 196d8cf..165697f 100644 --- a/runtime/src/stack/local_stack.rs +++ b/runtime/src/stack/local_stack.rs @@ -1,16 +1,23 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; +use std::fmt::Debug; use std::ops::{Index, IndexMut}; use common::box_slice; use instructions::Operand; // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-2.html#jvms-2.6.1 -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct LocalStack { inner: Box<[Operand]>, } +impl Debug for LocalStack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.inner.iter()).finish() + } +} + impl LocalStack { pub fn new(stack_size: usize) -> Self { Self { diff --git a/runtime/src/stack/operand_stack.rs b/runtime/src/stack/operand_stack.rs index b2bab28..5c4db7a 100644 --- a/runtime/src/stack/operand_stack.rs +++ b/runtime/src/stack/operand_stack.rs @@ -1,7 +1,8 @@ -use crate::reference::Reference; +use crate::objects::reference::Reference; -use common::int_types::{s4, s8}; +use std::fmt::Debug; +use common::int_types::{s4, s8}; use instructions::{ConstOperandType, Operand, StackLike}; macro_rules! trace_stack { @@ -25,11 +26,17 @@ macro_rules! trace_stack { } // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-2.html#jvms-2.6.2 -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct OperandStack { inner: Vec>, } +impl Debug for OperandStack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.inner.iter()).finish() + } +} + impl OperandStack { pub fn new(capacity: usize) -> Self { Self { diff --git a/runtime/src/string_interner.rs b/runtime/src/string_interner.rs index 895b6a4..c954f3a 100644 --- a/runtime/src/string_interner.rs +++ b/runtime/src/string_interner.rs @@ -1,6 +1,6 @@ -use crate::class_instance::{ArrayContent, ArrayInstance, ClassInstance, Instance}; use crate::classpath::classloader::ClassLoader; -use crate::reference::{ClassInstanceRef, Reference}; +use crate::objects::class_instance::{ArrayContent, ArrayInstance, ClassInstance, Instance}; +use crate::objects::reference::{ClassInstanceRef, Reference}; use std::collections::HashMap; use std::ptr::slice_from_raw_parts; @@ -74,14 +74,14 @@ impl StringInterner { // Set `private byte[] value` new_java_string_instance.get_mut().put_field_value0( - crate::globals::field_offsets::string_value_field_offset(), + crate::globals::field_offsets::java_lang_String::value_field_offset(), reference_to_byte_array, ); // Set `private final byte coder` let coder = if is_latin1 { CODER_LATIN1 } else { CODER_UTF16 }; new_java_string_instance.get_mut().put_field_value0( - crate::globals::field_offsets::string_coder_field_offset(), + crate::globals::field_offsets::java_lang_String::coder_field_offset(), Operand::Int(coder), ); @@ -95,12 +95,12 @@ impl StringInterner { } pub fn rust_string_from_java_string(class: ClassInstanceRef) -> String { - let string_value_field = class - .get() - .get_field_value0(crate::globals::field_offsets::string_value_field_offset()); + let string_value_field = class.get().get_field_value0( + crate::globals::field_offsets::java_lang_String::value_field_offset(), + ); let string_coder_field = class .get() - .get_field_value0(crate::globals::field_offsets::string_coder_field_offset()) + .get_field_value0(crate::globals::field_offsets::java_lang_String::coder_field_offset()) .expect_int(); let char_array = string_value_field.expect_reference().extract_array(); diff --git a/runtime/src/thread/exceptions.rs b/runtime/src/thread/exceptions.rs new file mode 100644 index 0000000..5ee2bf9 --- /dev/null +++ b/runtime/src/thread/exceptions.rs @@ -0,0 +1,71 @@ +use super::JavaThread; +use crate::objects::reference::Reference; +use crate::stack::local_stack::LocalStack; + +use std::sync::atomic::Ordering; + +use classfile::accessflags::MethodAccessFlags; +use common::traits::PtrType; +use instructions::{Operand, StackLike}; +use symbols::sym; + +/// See [`JavaThread::throw_exception`] +pub(super) fn throw(thread: &JavaThread, object_ref: Reference) { + // 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. + + let class_instance = object_ref.extract_class(); + + let throwable_class = crate::globals::classes::java_lang_Throwable(); + assert!( + class_instance.get().class() == throwable_class + || class_instance.get().is_subclass_of(&throwable_class) + ); + + // Search each frame for an exception handler + thread.stash_and_reset_pc(); + while let Some(current_frame) = thread.frame_stack.current() { + let current_frame_pc = current_frame.stashed_pc(); + + // If an exception handler that matches objectref is found, it contains the location of the code intended to handle this exception. + if let Some(handler_pc) = current_frame + .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 + // is pushed back onto the operand stack, and execution continues. + thread.pc.store(handler_pc, Ordering::Relaxed); + + let stack = current_frame.stack_mut(); + stack.clear(); + stack.push_reference(object_ref); + + return; + } + + let _ = thread.frame_stack.pop(); + } + + // No handler found, we have to print the stack trace and exit + thread.frame_stack.clear(); + print_stack_trace(thread, object_ref); +} + +fn print_stack_trace(thread: &JavaThread, object_ref: Reference) { + let print_stack_trace = object_ref + .extract_class() + .get() + .class() + .vtable() + .find( + sym!(printStackTrace_name), + sym!(void_method_signature), + MethodAccessFlags::NONE, + ) + .expect("java/lang/Throwable#printStackTrace should exist"); + + let mut locals = LocalStack::new(1); + locals[0] = Operand::Reference(object_ref); + + thread.invoke_method_with_local_stack(print_stack_trace, locals); +} diff --git a/runtime/src/frame.rs b/runtime/src/thread/frame/mod.rs similarity index 92% rename from runtime/src/frame.rs rename to runtime/src/thread/frame/mod.rs index 14097a0..da1bfd0 100644 --- a/runtime/src/frame.rs +++ b/runtime/src/thread/frame/mod.rs @@ -1,4 +1,8 @@ -use crate::method::Method; +pub mod native; +pub mod stack; + +use crate::objects::constant_pool::ConstantPool; +use crate::objects::method::Method; use crate::stack::local_stack::LocalStack; use crate::stack::operand_stack::OperandStack; use crate::thread::JavaThread; @@ -7,7 +11,6 @@ use std::cell::UnsafeCell; use std::fmt::{Debug, Formatter}; use std::sync::atomic::{AtomicIsize, Ordering}; -use classfile::ConstantPoolRef; use common::int_types::{s1, s2, s4, u1, u2, u4}; // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-2.html#jvms-2.6 @@ -20,7 +23,7 @@ pub struct Frame { // its own operand stack (§2.6.2) stack: OperandStack, // and a reference to the run-time constant pool (§2.5.5) - constant_pool: ConstantPoolRef, + constant_pool: &'static ConstantPool, method: &'static Method, thread: UnsafeCell<*const JavaThread>, @@ -45,9 +48,12 @@ impl Frame { thread: &JavaThread, locals: LocalStack, max_stack: u2, - constant_pool: ConstantPoolRef, method: &'static Method, ) -> Self { + let constant_pool = method + .class() + .constant_pool() + .expect("Methods do not exist on array classes"); Self { locals, stack: OperandStack::new(max_stack as usize), @@ -69,8 +75,8 @@ impl Frame { /// Get a reference to the constant pool #[inline] - pub fn constant_pool(&self) -> ConstantPoolRef { - self.constant_pool.clone() + pub fn constant_pool(&self) -> &'static ConstantPool { + self.constant_pool } /// Get a reference to the associated operand stack @@ -99,7 +105,7 @@ impl Frame { /// Get the method associated with this frame #[inline] - pub fn method(&self) -> &Method { + pub fn method(&self) -> &'static Method { self.method } diff --git a/runtime/src/thread/frame/native.rs b/runtime/src/thread/frame/native.rs new file mode 100644 index 0000000..a23a946 --- /dev/null +++ b/runtime/src/thread/frame/native.rs @@ -0,0 +1,19 @@ +use crate::objects::method::Method; + +/// A thin marker frame for native methods +/// +/// A stack frame is not usually necessary for native methods, but we still need to keep track of +/// the calls. +/// +/// These are useful for certain reflection methods (ex. `Reflection#getCallerClass`) and stacktraces. +#[derive(Debug)] +pub struct NativeFrame { + pub method: &'static Method, +} + +impl NativeFrame { + /// Get the method associated with this frame + pub fn method(&self) -> &'static Method { + self.method + } +} diff --git a/runtime/src/thread/frame/stack.rs b/runtime/src/thread/frame/stack.rs new file mode 100644 index 0000000..8f20fe2 --- /dev/null +++ b/runtime/src/thread/frame/stack.rs @@ -0,0 +1,128 @@ +use super::native::NativeFrame; +use super::Frame; +use crate::objects::method::Method; + +use std::cell::UnsafeCell; + +#[derive(Debug)] +pub enum StackFrame { + Real(Frame), + Native(NativeFrame), + Fake, +} + +impl StackFrame { + fn is_fake(&self) -> bool { + matches!(self, StackFrame::Fake) + } +} + +pub enum VisibleStackFrame<'a> { + Regular(&'a Frame), + Native(&'a NativeFrame), +} + +impl<'a> VisibleStackFrame<'a> { + pub fn method(&self) -> &'static Method { + match self { + VisibleStackFrame::Regular(frame) => frame.method(), + VisibleStackFrame::Native(frame) => frame.method(), + } + } +} + +impl<'a> From<&'a Frame> for VisibleStackFrame<'a> { + fn from(frame: &'a Frame) -> Self { + VisibleStackFrame::Regular(frame) + } +} + +impl<'a> From<&'a NativeFrame> for VisibleStackFrame<'a> { + fn from(frame: &'a NativeFrame) -> Self { + VisibleStackFrame::Native(frame) + } +} + +#[derive(Debug)] +pub struct FrameStack { + inner: UnsafeCell>, +} + +impl FrameStack { + // TODO + pub fn new() -> Self { + FrameStack { + inner: UnsafeCell::new(Vec::with_capacity(1024)), + } + } + + pub fn current(&self) -> Option<&mut Frame> { + let current_frame = self.__inner_mut().last_mut(); + match current_frame { + Some(StackFrame::Real(r)) => Some(r), + _ => None, + } + } + + pub fn depth(&self) -> usize { + self.__inner().len() + } + + pub fn iter(&self) -> impl DoubleEndedIterator> { + self.__inner().iter().filter_map(|frame| match frame { + StackFrame::Real(frame) => Some(VisibleStackFrame::Regular(frame)), + StackFrame::Native(frame) => Some(VisibleStackFrame::Native(frame)), + StackFrame::Fake => None, + }) + } + + pub fn get(&self, position: usize) -> Option> { + match self.__inner().get(position) { + Some(StackFrame::Real(frame)) => Some(VisibleStackFrame::from(frame)), + Some(StackFrame::Native(frame)) => Some(VisibleStackFrame::from(frame)), + Some(StackFrame::Fake) => self.get(position - 1), // TODO: HACK!!! + None => None, + } + } + + pub fn push(&self, frame: StackFrame) { + self.__inner_mut().push(frame); + } + + pub fn pop(&self) -> Option { + self.__inner_mut().pop() + } + + pub fn pop_real(&self) -> Option { + match self.__inner_mut().pop() { + Some(StackFrame::Real(r)) => Some(r), + _ => None, + } + } + + pub fn pop_native(&self) -> Option { + match self.__inner_mut().pop() { + Some(StackFrame::Native(r)) => Some(r), + _ => None, + } + } + + pub fn pop_dummy(&self) { + match self.__inner_mut().pop() { + Some(StackFrame::Fake) => return, + _ => panic!("Expected a dummy frame!"), + } + } + + pub fn clear(&self) { + self.__inner_mut().clear(); + } + + fn __inner(&self) -> &mut Vec { + unsafe { &mut *self.inner.get() } + } + + fn __inner_mut(&self) -> &mut Vec { + unsafe { &mut *self.inner.get() } + } +} diff --git a/runtime/src/thread/java_lang_Thread.rs b/runtime/src/thread/java_lang_Thread.rs new file mode 100644 index 0000000..21ab4de --- /dev/null +++ b/runtime/src/thread/java_lang_Thread.rs @@ -0,0 +1,166 @@ +//! Methods for interacting with `java.lang.Thread` instances + +use crate::objects::class_instance::Instance; +use crate::objects::reference::Reference; + +use common::traits::PtrType; +use instructions::Operand; +use symbols::sym; + +pub fn set_field_offsets() { + // java.lang.Thread fields + { + let class = crate::globals::classes::java_lang_Thread(); + + let mut field_set = 0; + for (index, field) in class.instance_fields().enumerate() { + if field.name == sym!(holder) { + unsafe { + crate::globals::field_offsets::java_lang_Thread::set_holder_field_offset(index); + } + + field_set |= 1; + continue; + } + + if field.name == sym!(eetop) { + unsafe { + crate::globals::field_offsets::java_lang_Thread::set_eetop_field_offset(index); + } + + field_set |= 1 << 1; + continue; + } + } + + assert_eq!( + field_set, 0b11, + "Not all fields were found in java/lang/Thread" + ); + } + + holder::set_field_holder_offsets(); +} + +pub(super) fn set_eetop(obj: Reference, eetop: jni::sys::jlong) { + let offset = crate::globals::field_offsets::java_lang_Thread::eetop_field_offset(); + + let instance = obj.extract_class(); + instance + .get_mut() + .put_field_value0(offset, Operand::Long(eetop)); +} + +/// java.lang.Thread$FieldHolder accessors +pub mod holder { + use crate::objects::class_instance::Instance; + use crate::objects::reference::Reference; + use crate::thread::ThreadStatus; + + use common::int_types::s4; + use common::traits::PtrType; + use instructions::Operand; + use jni::sys::jlong; + + pub(super) fn set_field_holder_offsets() { + let class = crate::globals::classes::java_lang_Thread_FieldHolder(); + + let mut field_set = 0; + for (index, field) in class.fields().enumerate() { + match field.name.as_str() { + "stackSize" => { + unsafe { + crate::globals::field_offsets::java_lang_Thread::holder::set_stack_size_field_offset( + index, + ); + } + field_set |= 1; + }, + "priority" => { + unsafe { + crate::globals::field_offsets::java_lang_Thread::holder::set_priority_field_offset( + index, + ); + } + field_set |= 1 << 1; + }, + "daemon" => { + unsafe { + crate::globals::field_offsets::java_lang_Thread::holder::set_daemon_field_offset(index); + } + field_set |= 1 << 2; + }, + "threadStatus" => { + unsafe { + crate::globals::field_offsets::java_lang_Thread::holder::set_thread_status_field_offset( + index, + ); + } + field_set |= 1 << 3; + }, + _ => {}, + } + } + + assert_eq!( + field_set, 0b1111, + "Not all fields were found in java/lang/Thread$FieldHolder" + ); + } + + fn get_field_holder_field(obj: &Reference, offset: usize) -> Operand { + let class_instance = obj.extract_class(); + + let field_holder_offset = + crate::globals::field_offsets::java_lang_Thread::holder_field_offset(); + let field_holder_ref = &class_instance + .get_mut() + .get_field_value0(field_holder_offset); + + let field_holder_instance = field_holder_ref.expect_reference().extract_class(); + field_holder_instance.get_mut().get_field_value0(offset) + } + + pub fn stack_size(obj: &Reference) -> jlong { + let offset = + crate::globals::field_offsets::java_lang_Thread::holder::stack_size_field_offset(); + get_field_holder_field(obj, offset).expect_long() + } + + fn set_field_holder_field(obj: Reference, offset: usize, value: Operand) { + let class_instance = obj.extract_class(); + + let field_holder_offset = + crate::globals::field_offsets::java_lang_Thread::holder_field_offset(); + let field_holder_ref = &class_instance + .get_mut() + .get_field_value0(field_holder_offset); + + let field_holder_instance = field_holder_ref.expect_reference().extract_class(); + field_holder_instance + .get_mut() + .put_field_value0(offset, value); + } + + pub fn set_stack_size(obj: Reference, stack_size: jlong) { + let offset = + crate::globals::field_offsets::java_lang_Thread::holder::stack_size_field_offset(); + set_field_holder_field(obj, offset, Operand::Long(stack_size)); + } + + pub fn set_priority(obj: Reference, priority: s4) { + let offset = + crate::globals::field_offsets::java_lang_Thread::holder::priority_field_offset(); + set_field_holder_field(obj, offset, Operand::Int(priority)); + } + + pub(in crate::thread) fn set_daemon(_obj: Reference, _daemon: bool) { + todo!() + } + + pub fn set_thread_status(obj: Reference, thread_status: ThreadStatus) { + let offset = + crate::globals::field_offsets::java_lang_Thread::holder::thread_status_field_offset(); + set_field_holder_field(obj, offset, Operand::Int(thread_status as s4)); + } +} diff --git a/runtime/src/thread.rs b/runtime/src/thread/mod.rs similarity index 55% rename from runtime/src/thread.rs rename to runtime/src/thread/mod.rs index 84b92ff..e680983 100644 --- a/runtime/src/thread.rs +++ b/runtime/src/thread/mod.rs @@ -1,27 +1,37 @@ -use crate::class_instance::{ClassInstance, Instance}; -use crate::frame::Frame; +mod exceptions; +pub mod frame; +use frame::stack::{FrameStack, StackFrame}; +#[allow(non_snake_case)] +pub mod java_lang_Thread; +pub mod pool; + +use crate::classpath::classloader::ClassLoader; use crate::interpreter::Interpreter; use crate::java_call; -use crate::method::Method; use crate::native::jni::invocation_api::new_env; -use crate::reference::{ClassInstanceRef, Reference}; +use crate::objects::class_instance::ClassInstance; +use crate::objects::method::Method; +use crate::objects::reference::{ClassInstanceRef, Reference}; use crate::stack::local_stack::LocalStack; use crate::string_interner::StringInterner; +use crate::thread::frame::native::NativeFrame; +use crate::thread::frame::Frame; +use crate::thread::pool::ThreadPool; use std::cell::{SyncUnsafeCell, UnsafeCell}; use std::ptr::NonNull; use std::sync::atomic::{AtomicIsize, Ordering}; use std::sync::Arc; +use std::thread; +use std::thread::JoinHandle; use classfile::accessflags::MethodAccessFlags; -use common::int_types::s4; -use common::traits::PtrType; use instructions::{Operand, StackLike}; use jni::env::JniEnv; use symbols::sym; #[thread_local] -static CURRENT_JAVA_THREAD: SyncUnsafeCell> = SyncUnsafeCell::new(None); +static CURRENT_JAVA_THREAD: SyncUnsafeCell> = SyncUnsafeCell::new(None); pub struct JVMOptions { pub dry_run: bool, @@ -30,90 +40,95 @@ pub struct JVMOptions { pub show_version: bool, } -#[derive(Debug)] -enum StackFrame { - Real(Frame), - Fake, +/// A builder for a `JavaThread` +/// +/// This is the only way to construct a `JavaThread`, and is responsible for spawning the associated +/// OS thread, if applicable. +#[derive(Default)] +pub struct JavaThreadBuilder { + obj: Option, + entry_point: Option>, + stack_size: usize, } -impl StackFrame { - fn is_fake(&self) -> bool { - matches!(self, StackFrame::Fake) - } -} - -#[derive(Debug)] -pub struct FrameStack { - inner: UnsafeCell>, -} - -impl FrameStack { - // TODO - fn new() -> Self { - FrameStack { - inner: UnsafeCell::new(Vec::with_capacity(1024)), +impl JavaThreadBuilder { + /// Create a new `JavaThreadBuilder` + /// + /// This is equivalent to [`Self::default`]. + pub fn new() -> JavaThreadBuilder { + Self { + obj: None, + entry_point: None, + stack_size: 0, // TODO: Default -Xss } } - fn current(&self) -> Option<&mut Frame> { - let current_frame = self.__inner_mut().last_mut(); - match current_frame { - Some(StackFrame::Real(r)) => Some(r), - _ => None, - } + // TODO: Unsafe? The object is not verified to be of the correct class. + /// Set the `java.lang.Thread` associated with this `JavaThread` + /// + /// It is up to the caller to verify that `obj` is *actually* of the correct type. + pub fn obj(mut self, obj: Reference) -> Self { + self.obj = Some(obj); + self } - pub fn depth(&self) -> usize { - self.__inner().len() + /// Set the entrypoint of this `JavaThread` + /// + /// Setting this will spawn an OS thread to run `entry`. This is really only used with [`JavaThread::default_entry_point`], + /// which calls `java.lang.Thread#run` on the associated [`obj`]. + /// + /// [`obj`]: Self::obj + pub fn entry_point(mut self, entry: impl Fn(&JavaThread) + Send + Sync + 'static) -> Self { + self.entry_point = Some(Box::new(entry)); + self } - pub fn iter(&self) -> impl DoubleEndedIterator { - self.__inner().iter().filter_map(|frame| match frame { - StackFrame::Real(frame) => Some(frame), - StackFrame::Fake => None, - }) + /// Set the stack size of the associated OS thread + /// + /// This will have no effect if there is no [`entry_point`] set. + /// + /// [`entry_point`]: Self::entry_point + pub fn stack_size(mut self, size: usize) -> Self { + self.stack_size = size; + self } - pub fn get(&self, position: usize) -> Option<&Frame> { - match self.__inner().get(position) { - Some(StackFrame::Real(frame)) => Some(frame), - None => None, - _ => unreachable!(), - } - } + /// Construct the `JavaThread` + /// + /// This will also spawn an OS thread if applicable. + /// + /// The return type of this depends on whether an OS thread has been spawned. If no [`entry_point`] + /// was set, it is safe to unwrap it as [`MaybeArc::Not`]. + /// + /// [`entry_point`]: Self::entry_point + pub fn finish(self) -> &'static JavaThread { + let thread = JavaThread { + env: unsafe { JniEnv::from_raw(new_env()) }, + obj: UnsafeCell::new(self.obj), + os_thread: UnsafeCell::new(None), - fn push(&self, frame: StackFrame) { - self.__inner_mut().push(frame); - } + pc: AtomicIsize::new(0), + frame_stack: FrameStack::new(), + remaining_operand: UnsafeCell::new(None), + }; - fn pop(&self) -> Option { - self.__inner_mut().pop() - } + let thread = ThreadPool::push(thread); + if let Some(entry_point) = self.entry_point { + let mut os_thread = thread::Builder::new(); + if self.stack_size > 0 { + os_thread = os_thread.stack_size(self.stack_size); + } - fn pop_real(&self) -> Option { - match self.__inner_mut().pop() { - Some(StackFrame::Real(r)) => Some(r), - _ => None, - } - } + let os_thread_ptr = thread.os_thread.get(); - fn pop_dummy(&self) { - match self.__inner_mut().pop() { - Some(StackFrame::Fake) => return, - _ => panic!("Expected a dummy frame!"), + // TODO: Error handling + let handle = os_thread.spawn(move || entry_point(thread)).unwrap(); + unsafe { + *os_thread_ptr = Some(handle); + } } - } - fn clear(&self) { - self.__inner_mut().clear(); - } - - fn __inner(&self) -> &mut Vec { - unsafe { &mut *self.inner.get() } - } - - fn __inner_mut(&self) -> &mut Vec { - unsafe { &mut *self.inner.get() } + thread } } @@ -121,6 +136,7 @@ impl FrameStack { pub struct JavaThread { env: JniEnv, obj: UnsafeCell>, + os_thread: UnsafeCell>>, // https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-2.html#jvms-2.5.1 // Each Java Virtual Machine thread has its own pc (program counter) register [...] @@ -131,6 +147,9 @@ pub struct JavaThread { remaining_operand: UnsafeCell>>, } +unsafe impl Sync for JavaThread {} +unsafe impl Send for JavaThread {} + impl PartialEq for JavaThread { fn eq(&self, other: &Self) -> bool { self.env() == other.env() @@ -158,7 +177,7 @@ impl JavaThread { // SAFETY: The thread is an `Option`, so it's always initialized with *something* let opt = unsafe { &*current }; - match opt { + match *opt { None => std::ptr::null(), Some(thread) => thread, } @@ -182,7 +201,7 @@ impl JavaThread { // SAFETY: The thread is an `Option`, so it's always initialized with *something* let opt = unsafe { &*current }; - opt.as_ref() + *opt } /// Sets the current Java [`JavaThread`] @@ -190,7 +209,7 @@ impl JavaThread { /// # Panics /// /// This will panic if there is already a current thread set - pub unsafe fn set_current_thread(thread: JavaThread) { + pub unsafe fn set_current_thread(thread: &'static JavaThread) { let current = CURRENT_JAVA_THREAD.get(); // SAFETY: The thread is an `Option`, so it's always initialized with *something* @@ -221,139 +240,23 @@ pub enum ThreadStatus { Terminated = 8, } -/// `java.lang.Thread$FieldHolder` accessors -pub mod java_lang_Thread { - use crate::class_instance::Instance; - use crate::reference::Reference; - use crate::thread::ThreadStatus; - use crate::JavaThread; - use common::int_types::s4; - use common::traits::PtrType; - use instructions::Operand; - use symbols::sym; - - pub fn set_field_offsets() { - // java.lang.Thread fields - { - let class = crate::globals::classes::java_lang_Thread(); - - let mut field_set = 0; - for (index, field) in class.instance_fields().enumerate() { - if field.name == sym!(holder) { - unsafe { - crate::globals::field_offsets::set_thread_holder_field_offset(index); - } - - field_set |= 1; - continue; - } - - if field.name == sym!(eetop) { - unsafe { - crate::globals::field_offsets::set_thread_eetop_field_offset(index); - } - - field_set |= 1 << 1; - continue; - } - } - - assert_eq!( - field_set, 0b11, - "Not all fields were found in java/lang/Thread" - ); - } - - set_field_holder_offsets(); - } - - // java.lang.Thread$FieldHolder fields - fn set_field_holder_offsets() { - let class = crate::globals::classes::java_lang_Thread_FieldHolder(); - - let mut field_set = 0; - for (index, field) in class.fields().enumerate() { - match field.name.as_str() { - "priority" => unsafe { - crate::globals::field_offsets::set_field_holder_priority_field_offset(index); - field_set |= 1; - }, - "daemon" => unsafe { - crate::globals::field_offsets::set_field_holder_daemon_field_offset(index); - field_set |= 1 << 1; - }, - "threadStatus" => unsafe { - crate::globals::field_offsets::set_field_holder_thread_status_field_offset( - index, - ); - field_set |= 1 << 2; - }, - _ => {}, - } - } - - assert_eq!( - field_set, 0b111, - "Not all fields were found in java/lang/Thread$FieldHolder" - ); - } - - pub(super) fn set_eetop(obj: Reference, eetop: jni::sys::jlong) { - let offset = crate::globals::field_offsets::thread_eetop_field_offset(); - - let instance = obj.extract_class(); - instance - .get_mut() - .put_field_value0(offset, Operand::Long(eetop)); - } - - fn set_field_holder_field(obj: Reference, offset: usize, value: Operand) { - let class_instance = obj.extract_class(); - - let field_holder_offset = crate::globals::field_offsets::thread_holder_field_offset(); - let field_holder_ref = &class_instance - .get_mut() - .get_field_value0(field_holder_offset); - - let field_holder_instance = field_holder_ref.expect_reference().extract_class(); - field_holder_instance - .get_mut() - .put_field_value0(offset, value); - } - - pub fn set_priority(obj: Reference, priority: s4) { - let offset = crate::globals::field_offsets::field_holder_priority_field_offset(); - set_field_holder_field(obj, offset, Operand::Int(priority)); - } - - fn set_daemon(_obj: Reference, _daemon: bool) { - todo!() - } - - pub(super) fn set_thread_status(obj: Reference, thread_status: ThreadStatus) { - let offset = crate::globals::field_offsets::field_holder_thread_status_field_offset(); - set_field_holder_field(obj, offset, Operand::Int(thread_status as s4)); - } -} - // Actions for the related `java.lang.Thread` instance impl JavaThread { - /// Get the `JavaThread` associated with `obj` + /// The default entrypoint for a `java.lang.Thread` /// - /// # Safety + /// This simply calls `java.lang.Thread#run` with the [`obj`] associated with this `JavaThread`. /// - /// The caller must ensure that `obj` is a `java.lang.Thread` object obtained from [`Self::obj()`] - pub unsafe fn for_obj(obj: ClassInstanceRef) -> Option<*mut JavaThread> { - let eetop_offset = crate::globals::field_offsets::thread_eetop_field_offset(); - let field_value_ptr = unsafe { obj.get().get_field_value_raw(eetop_offset) }; - let field_value = unsafe { field_value_ptr.as_ref() }; - let eetop = field_value.expect_long(); - if eetop == 0 { - // Thread is not alive - return None; - } + /// [`obj`]: JavaThread::obj + pub fn default_entry_point(&self) { + let obj = self.obj().expect("entrypoint should exist"); - Some(eetop as *mut JavaThread) + let thread_class = ClassLoader::Bootstrap.load(sym!(java_lang_Thread)).unwrap(); + let run_method = thread_class + .resolve_method_step_two(sym!(run_name), sym!(void_method_signature)) + .unwrap(); + + let ret = java_call!(self, run_method, Operand::Reference(obj)); + assert!(ret.is_none()); } /// Allocates a new `java.lang.Thread` for this `JavaThread` @@ -436,7 +339,7 @@ impl JavaThread { Operand::Reference(Reference::class(thread_name)), ); - java_lang_Thread::set_thread_status(obj, ThreadStatus::Runnable); + java_lang_Thread::holder::set_thread_status(obj, ThreadStatus::Runnable); } pub fn set_obj(&self, obj: Reference) { @@ -461,17 +364,6 @@ impl JavaThread { } impl JavaThread { - pub fn new() -> Self { - Self { - env: unsafe { JniEnv::from_raw(new_env()) }, - obj: UnsafeCell::new(None), - - pc: AtomicIsize::new(0), - frame_stack: FrameStack::new(), - remaining_operand: UnsafeCell::new(None), - } - } - /// Get a pointer to the associated [`JniEnv`] pub fn env(&self) -> NonNull { unsafe { @@ -538,23 +430,30 @@ impl JavaThread { let max_stack = method.code.max_stack; - let constant_pool = Arc::clone(&method.class.unwrap_class_instance().constant_pool); - - let frame = Frame::new(self, locals, max_stack, constant_pool, method); + let frame = Frame::new(self, locals, max_stack, method); self.stash_and_reset_pc(); self.frame_stack.push(StackFrame::Real(frame)); } - // Native methods do not require a stack frame. We just call and leave the stack behind until we return. - fn invoke_native(&self, method: &Method, locals: LocalStack) { + fn invoke_native(&self, method: &'static Method, locals: LocalStack) { // Try to lookup and set the method prior to calling crate::native::lookup::lookup_native_method(method, self); let fn_ptr = super::native::lookup_method(method); - // Push the return value onto the frame's stack - if let Some(ret) = fn_ptr(self.env(), locals) { + // See comments on `NativeFrame` + self.frame_stack + .push(StackFrame::Native(NativeFrame { method })); + + let ret = fn_ptr(self.env(), locals); + assert!( + self.frame_stack.pop_native().is_some(), + "native frame consumed" + ); + + // 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); } @@ -612,67 +511,6 @@ 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) { - // 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. - if object_ref.is_null() { - self.throw_npe(); - return; - } - - let class_instance = object_ref.extract_class(); - - let throwable_class = crate::globals::classes::java_lang_Throwable(); - assert!( - class_instance.get().class() == throwable_class - || class_instance.get().is_subclass_of(&throwable_class) - ); - - // Search each frame for an exception handler - self.stash_and_reset_pc(); - while let Some(current_frame) = self.frame_stack.current() { - let current_frame_pc = current_frame.stashed_pc(); - - // If an exception handler that matches objectref is found, it contains the location of the code intended to handle this exception. - if let Some(handler_pc) = current_frame - .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 - // is pushed back onto the operand stack, and execution continues. - self.pc.store(handler_pc, Ordering::Relaxed); - - let stack = current_frame.stack_mut(); - stack.clear(); - stack.push_reference(object_ref); - - return; - } - - let _ = self.frame_stack.pop(); - } - - // No handler found, we have to print the stack trace and exit - self.frame_stack.clear(); - - let print_stack_trace = class_instance - .get() - .class() - .vtable() - .find( - sym!(printStackTrace_name), - sym!(void_method_signature), - MethodAccessFlags::NONE, - ) - .expect("java/lang/Throwable#printStackTrace should exist"); - - let mut locals = LocalStack::new(1); - locals[0] = Operand::Reference(object_ref); - - self.invoke_method_with_local_stack(print_stack_trace, locals); - } - - /// Throw a `NullPointerException` on this thread - pub fn throw_npe(&self) { - todo!() + exceptions::throw(self, object_ref); } } diff --git a/runtime/src/thread/pool.rs b/runtime/src/thread/pool.rs new file mode 100644 index 0000000..f8252bc --- /dev/null +++ b/runtime/src/thread/pool.rs @@ -0,0 +1,63 @@ +use super::JavaThread; +use crate::objects::class_instance::Instance; +use crate::objects::reference::Reference; + +use std::sync::RwLock; + +static VM_THREAD_POOL: RwLock = RwLock::new(ThreadPool::new()); + +pub struct ThreadPool { + threads: Vec<&'static JavaThread>, +} + +impl ThreadPool { + const fn new() -> ThreadPool { + Self { + threads: Vec::new(), + } + } + + /// Add a thread to the pool + pub fn push(thread: JavaThread) -> &'static JavaThread { + let mut guard = VM_THREAD_POOL.write().unwrap(); + let thread = Box::leak(Box::new(thread)); + guard.threads.push(thread); + thread + } + + /// Whether `thread` is in this pool + pub fn contains(thread: &JavaThread) -> bool { + VM_THREAD_POOL.read().unwrap().threads.contains(&thread) + } + + /// Find the [`JavaThread`] associated with `obj` + /// + /// This is the only safe way to relate `java.lang.Thread` objects to their internal [`JavaThread`] + /// counterparts. However, if the associated [`JavaThread`] is *not* in this pool, this method will + /// not be able to find it. + pub fn find_from_obj(obj: Reference) -> Option<&'static JavaThread> { + let eetop_offset = crate::globals::field_offsets::java_lang_Thread::eetop_field_offset(); + let field_value_ptr = unsafe { obj.get_field_value_raw(eetop_offset) }; + let field_value = unsafe { field_value_ptr.as_ref() }; + let eetop = field_value.expect_long(); + if eetop == 0 { + // Thread is not alive + return None; + } + + // SAFETY: This isn't really safe, but we have to assume that the pointer at `eetop` is a valid + // one that we set. + let java_thread = unsafe { &*(eetop as *mut JavaThread) }; + + let current = JavaThread::current(); + if java_thread == current { + return Some(current); + } + + if ThreadPool::contains(&java_thread) { + return Some(java_thread); + } + + None + } +} diff --git a/runtime/src/verifier/accessors.rs b/runtime/src/verifier/accessors.rs index 23555d9..0eb98e1 100644 --- a/runtime/src/verifier/accessors.rs +++ b/runtime/src/verifier/accessors.rs @@ -1,9 +1,9 @@ //! (§4.10.1.1) Accessors for Java Virtual Machine Artifacts use super::type_system::VerificationType; -use crate::class::Class; -use crate::method::Method; -use crate::vtable::VTable; +use crate::objects::class::Class; +use crate::objects::method::Method; +use crate::objects::vtable::VTable; use classfile::accessflags::MethodAccessFlags; use classfile::{Attribute, MethodDescriptor}; diff --git a/runtime/src/verifier/method.rs b/runtime/src/verifier/method.rs index d17d144..962c4bc 100644 --- a/runtime/src/verifier/method.rs +++ b/runtime/src/verifier/method.rs @@ -1,8 +1,9 @@ use super::accessors::MethodAccessorExt; use super::error::{Error, Result}; use super::type_system::{types, IsAssignable, VerificationType}; -use crate::class::Class; -use crate::method::Method; +use crate::objects::class::Class; +use crate::objects::constant_pool::cp_types; +use crate::objects::method::Method; use classfile::{Attribute, CodeException, StackMapTable}; use common::int_types::{u1, u2}; @@ -51,7 +52,7 @@ impl MethodTypeCheckExt for Method { return Err(Error::FinalMethodOverridden); } - code_is_type_safe(&self.class, self) + code_is_type_safe(&self.class(), self) } fn does_not_override_final_method(&self) -> bool { @@ -153,9 +154,8 @@ impl Environment<'_> { .class .constant_pool() .unwrap() - .get_constant_utf8(handler.catch_type); + .get::(handler.catch_type); - let exception_class_name = Symbol::intern_bytes(exception_class_name); let exception_class = types::Class(exception_class_name); if !exception_class.is_assignable(types::Class(sym!(java_lang_Throwable))) { return Err(Error::HandlerNotThrowable); diff --git a/runtime/src/verifier/mod.rs b/runtime/src/verifier/mod.rs index 829b95b..a40deec 100644 --- a/runtime/src/verifier/mod.rs +++ b/runtime/src/verifier/mod.rs @@ -3,7 +3,7 @@ mod error; mod method; mod type_system; -use crate::class::Class; +use crate::objects::class::Class; use crate::verifier::accessors::ClassAccessorExt; use crate::verifier::method::MethodTypeCheckExt; use error::{Error, Result}; diff --git a/symbols/src/lib.rs b/symbols/src/lib.rs index 1f41961..33eb1c8 100644 --- a/symbols/src/lib.rs +++ b/symbols/src/lib.rs @@ -140,12 +140,27 @@ macro_rules! sym { // Other generators may inject into this macro, take note of the marker comments below vm_symbols::define_symbols! { // Classes + + // Primitive + java_lang_Boolean: "java/lang/Boolean", + java_lang_Byte: "java/lang/Byte", + java_lang_Character: "java/lang/Character", + java_lang_Double: "java/lang/Double", + java_lang_Float: "java/lang/Float", + java_lang_Integer: "java/lang/Integer", + java_lang_Long: "java/lang/Long", + java_lang_Short: "java/lang/Short", + java_lang_Void: "java/lang/Void", + java_lang_Class: "java/lang/Class", java_lang_Object: "java/lang/Object", java_lang_String: "java/lang/String", + java_lang_Module: "java/lang/Module", java_lang_System: "java/lang/System", java_lang_Cloneable: "java/lang/Cloneable", java_io_Serializable: "java/io/Serializable", + java_io_File: "java/io/File", + jdk_internal_reflect_MethodAccessorImpl: "jdk/internal/reflect/MethodAccessorImpl", java_lang_StackTraceElement: "java/lang/StackTraceElement", java_lang_invoke_MethodHandle: "java/lang/invoke/MethodHandle", java_lang_invoke_VarHandle: "java/lang/invoke/VarHandle", @@ -198,14 +213,18 @@ vm_symbols::define_symbols! { printStackTrace_name: "printStackTrace", findNative_name: "findNative", + run_name: "run", // -- GENERATED METHOD NAME MARKER, DO NOT DELETE -- // Fields + name: "name", + loader: "loader", holder: "holder", eetop: "eetop", value: "value", coder: "coder", fd: "fd", + path: "path", r#in: "in", out: "out", err: "err", diff --git a/tools/sj/src/cli.rs b/tools/sj/src/cli.rs index 26176c9..2647adb 100644 --- a/tools/sj/src/cli.rs +++ b/tools/sj/src/cli.rs @@ -63,9 +63,9 @@ pub struct JVMOptions { pub show_version: bool, } -impl Into for JVMOptions { - fn into(self) -> jvm_runtime::JVMOptions { - jvm_runtime::JVMOptions { +impl Into for JVMOptions { + fn into(self) -> jvm_runtime::thread::JVMOptions { + jvm_runtime::thread::JVMOptions { dry_run: self.dry_run, system_properties: self.system_properties, showversion: self.showversion,