From d131a4119d99481631f570f50aee2fd31990f094 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:37:53 -0500 Subject: [PATCH] wip --- classfile/src/accessflags.rs | 17 ++ classfile/src/constant_pool/types.rs | 28 +- generators/native_methods/src/field.rs | 6 +- runtime/src/globals/classes.rs | 2 + runtime/src/globals/fields.rs | 48 ++++ runtime/src/initialization.rs | 16 ++ .../java/lang/invoke/MethodHandleNatives.rs | 269 ++++++++++++++++-- .../lang/invoke/def/MethodHandleNatives.def | 16 ++ runtime/src/objects/class/mod.rs | 18 ++ runtime/src/objects/constant_pool/cp_types.rs | 124 ++++---- runtime/src/objects/constant_pool/entry.rs | 1 + runtime/src/objects/field.rs | 8 + runtime/src/objects/method/mod.rs | 9 +- runtime/src/thread/exceptions.rs | 2 +- symbols/src/lib.rs | 69 +++-- 15 files changed, 501 insertions(+), 132 deletions(-) diff --git a/classfile/src/accessflags.rs b/classfile/src/accessflags.rs index 6bc9149..d5982e8 100644 --- a/classfile/src/accessflags.rs +++ b/classfile/src/accessflags.rs @@ -115,6 +115,11 @@ impl ClassAccessFlags { pub const ACC_ENUM : ClassAccessFlags = Self(0x4000); /// Is a module, not a class or interface. pub const ACC_MODULE : ClassAccessFlags = Self(0x8000); + + /// Returns the `u2` representation of the access flags + pub fn as_u2(self) -> u16 { + self.0 + } } impl_accessflags_for!(ClassAccessFlags); @@ -135,6 +140,7 @@ impl_is_methods! { /// Method access and property flags (§4.6-A) #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] pub struct MethodAccessFlags(u16); #[rustfmt::skip] @@ -167,6 +173,11 @@ impl MethodAccessFlags { pub const ACC_STRICT : MethodAccessFlags = Self(0x0800); /// Declared synthetic; not present in the source code. pub const ACC_SYNTHETIC : MethodAccessFlags = Self(0x1000); + + /// Returns the `u2` representation of the access flags + pub fn as_u2(self) -> u16 { + self.0 + } } impl_accessflags_for!(MethodAccessFlags); @@ -190,6 +201,7 @@ impl_is_methods! { /// Field access and property flags (§4.5-A) #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] pub struct FieldAccessFlags(u16); #[rustfmt::skip] @@ -215,6 +227,11 @@ impl FieldAccessFlags { pub const ACC_SYNTHETIC: FieldAccessFlags = Self(0x1000); /// Declared as an element of an enum class. pub const ACC_ENUM : FieldAccessFlags = Self(0x4000); + + /// Returns the `u2` representation of the access flags + pub fn as_u2(self) -> u16 { + self.0 + } } impl_accessflags_for!(FieldAccessFlags); diff --git a/classfile/src/constant_pool/types.rs b/classfile/src/constant_pool/types.rs index ed94d63..05286ec 100644 --- a/classfile/src/constant_pool/types.rs +++ b/classfile/src/constant_pool/types.rs @@ -62,7 +62,7 @@ impl<'a> FieldRefEntry<'a> { } /// The type of a method handle -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ReferenceKind { /// REF_getField GetField = 1, @@ -85,7 +85,7 @@ pub enum ReferenceKind { } impl ReferenceKind { - fn from_u8(value: u8) -> Option { + pub fn from_u8(value: u8) -> Option { match value { 1 => Some(ReferenceKind::GetField), 2 => Some(ReferenceKind::GetStatic), @@ -99,6 +99,30 @@ impl ReferenceKind { _ => None, } } + + pub fn is_field(self) -> bool { + matches!( + self, + ReferenceKind::GetField + | ReferenceKind::GetStatic + | ReferenceKind::PutField + | ReferenceKind::PutStatic + ) + } + + pub fn is_method(self) -> bool { + matches!( + self, + ReferenceKind::InvokeVirtual + | ReferenceKind::InvokeStatic + | ReferenceKind::InvokeSpecial + | ReferenceKind::InvokeInterface + ) + } + + pub fn is_constructor(self) -> bool { + self == ReferenceKind::NewInvokeSpecial + } } pub enum ReferenceEntry<'a> { diff --git a/generators/native_methods/src/field.rs b/generators/native_methods/src/field.rs index 38b2454..d56cd01 100644 --- a/generators/native_methods/src/field.rs +++ b/generators/native_methods/src/field.rs @@ -9,7 +9,11 @@ fn class_contains_fields(class: &Class) -> bool { for member in &class.members { match member { Member::Field(_) => return true, - Member::Class(c) => return class_contains_fields(c), + Member::Class(c) => { + if class_contains_fields(c) { + return true; + } + }, Member::Method(_) => {}, } } diff --git a/runtime/src/globals/classes.rs b/runtime/src/globals/classes.rs index f176a2d..d3f90a7 100644 --- a/runtime/src/globals/classes.rs +++ b/runtime/src/globals/classes.rs @@ -57,6 +57,8 @@ define_classes!( java_io_Serializable, java_lang_Module, java_lang_invoke_MethodHandleNatives, + java_lang_invoke_MemberName, + java_lang_invoke_ResolvedMethodName, java_lang_ref_Reference, java_lang_ref_Finalizer, java_io_FileDescriptor, diff --git a/runtime/src/globals/fields.rs b/runtime/src/globals/fields.rs index 43cf56d..785e621 100644 --- a/runtime/src/globals/fields.rs +++ b/runtime/src/globals/fields.rs @@ -595,3 +595,51 @@ pub mod jdk_internal_misc_UnsafeConstants { // TODO: class.set_static_field(data_cache_line_flush_size_field_offset(), /* ... */); } } + +pub mod java_lang_invoke_MemberName { + use classfile::FieldType; + use instructions::Operand; + use jni::sys::jint; + + field_module! { + @CLASS java_lang_invoke_MemberName; + + @FIELDSTART + /// `java.lang.invoke.MemberName#clazz` field offset + /// + /// Expected field type: `Reference` to `java.lang.Class` + @FIELD clazz: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/Class"), + /// `java.lang.invoke.MemberName#name` field offset + /// + /// Expected field type: `Reference` to `java.lang.String` + @FIELD name: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/String"), + /// `java.lang.invoke.MemberName#type` field offset + /// + /// Expected field type: `Reference` to `java.lang.Object` + @FIELD r#type: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/Object"), + /// `java.lang.invoke.MemberName#flags` field offset + /// + /// Expected field type: jint + @FIELD flags: FieldType::Int, + /// `java.lang.invoke.MemberName#method` field offset + /// + /// Expected field type: `Reference` to `java.lang.invoke.ResolvedMethodName` + @FIELD method: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/invoke/ResolvedMethodName"), + } +} + +pub mod java_lang_invoke_ResolvedMethodName { + use classfile::FieldType; + use instructions::Operand; + use jni::sys::jint; + + field_module! { + @CLASS java_lang_invoke_ResolvedMethodName; + + @FIELDSTART + /// `java.lang.invoke.ResolvedMethodName#vmholder` field offset + /// + /// Expected field type: `Reference` to `java.lang.Class` + @FIELD vmholder: ty @ FieldType::Object(_) if ty.is_class(b"java/lang/Class"), + } +} diff --git a/runtime/src/initialization.rs b/runtime/src/initialization.rs index 55fb4e9..6862e54 100644 --- a/runtime/src/initialization.rs +++ b/runtime/src/initialization.rs @@ -112,8 +112,14 @@ fn load_global_classes() { java_io_Serializable, java_lang_ref_Reference, java_lang_ref_Finalizer, + ); + + // MethodHandle stuff + load!( jdk_internal_reflect_MethodAccessorImpl, java_lang_invoke_MethodHandleNatives, + java_lang_invoke_MemberName, + java_lang_invoke_ResolvedMethodName, ); // Primitive types @@ -178,6 +184,16 @@ fn init_field_offsets() { unsafe { crate::globals::fields::java_lang_Thread::init_offsets(); } + + // java.lang.invoke.MemberName + unsafe { + crate::globals::fields::java_lang_invoke_MemberName::init_offsets(); + } + + // java.lang.invoke.ResolvedMethodName + unsafe { + crate::globals::fields::java_lang_invoke_ResolvedMethodName::init_offsets(); + } } fn initialize_global_classes(thread: &JavaThread) { diff --git a/runtime/src/native/java/lang/invoke/MethodHandleNatives.rs b/runtime/src/native/java/lang/invoke/MethodHandleNatives.rs index 39529ed..075f3a9 100644 --- a/runtime/src/native/java/lang/invoke/MethodHandleNatives.rs +++ b/runtime/src/native/java/lang/invoke/MethodHandleNatives.rs @@ -1,19 +1,184 @@ +use crate::native::java::lang::invoke::MethodHandleNatives; use crate::objects::class::Class; -use crate::objects::reference::Reference; +use crate::objects::class_instance::ClassInstance; +use crate::objects::instance::Instance; +use crate::objects::reference::{ClassInstanceRef, Reference}; +use crate::string_interner::StringInterner; +use crate::thread::exceptions::{throw, throw_with_ret, Throws}; +use crate::thread::JavaThread; -use jni::env::JniEnv; -use jni::sys::{jboolean, jint, jlong}; +use crate::objects::method::Method; +use ::jni::env::JniEnv; +use ::jni::sys::{jboolean, jint, jlong}; +use classfile::constant_pool::types::ReferenceKind; +use common::traits::PtrType; +use instructions::Operand; +use symbols::{sym, Symbol}; include_generated!("native/java/lang/invoke/def/MethodHandleNatives.registerNatives.rs"); include_generated!("native/java/lang/invoke/def/MethodHandleNatives.definitions.rs"); +include_generated!("native/java/lang/invoke/def/MethodHandleNatives$Constants.constants.rs"); + +pub fn new_member_name( + name: Symbol, + descriptor: Symbol, + callee_class: &'static Class, +) -> Throws { + let member_name_instance = + ClassInstance::new(crate::globals::classes::java_lang_invoke_MemberName()); + + let member_name = member_name_instance.get_mut(); + + member_name.put_field_value0( + crate::globals::fields::java_lang_invoke_MemberName::clazz_field_offset(), + Operand::Reference(Reference::mirror(callee_class.mirror())), + ); + member_name.put_field_value0( + crate::globals::fields::java_lang_invoke_MemberName::name_field_offset(), + Operand::Reference(Reference::class(StringInterner::intern_symbol(name))), + ); + member_name.put_field_value0( + crate::globals::fields::java_lang_invoke_MemberName::type_field_offset(), + Operand::Reference(Reference::class(StringInterner::intern_symbol(descriptor))), + ); + + Throws::Ok(member_name_instance) +} + +pub fn resolve_member_name( + member_name: &mut ClassInstance, + ref_kind: ReferenceKind, + calling_class: &'static Class, + lookup_mode: jint, +) -> Throws<()> { + let mut is_valid = true; + let mut flags = 0; + + let invoking_class_field = member_name + .get_field_value0(crate::globals::fields::java_lang_invoke_MemberName::clazz_field_offset()) + .expect_reference() + .extract_mirror(); + if invoking_class_field.get().is_primitive() { + throw!(@DEFER InternalError, "primitive class"); + } + let invoking_class = invoking_class_field.get().target_class(); + + let name_field = member_name + .get_field_value0(crate::globals::fields::java_lang_invoke_MemberName::name_field_offset()) + .expect_reference(); + let name_str = StringInterner::rust_string_from_java_string(name_field.extract_class()); + let name = Symbol::intern_owned(name_str); + + let descriptor_field = member_name + .get_field_value0(crate::globals::fields::java_lang_invoke_MemberName::type_field_offset()) + .expect_reference(); + let descriptor_str = + StringInterner::rust_string_from_java_string(descriptor_field.extract_class()); + let descriptor = Symbol::intern_owned(descriptor_str); + + match ref_kind { + ReferenceKind::GetField + | ReferenceKind::GetStatic + | ReferenceKind::PutField + | ReferenceKind::PutStatic => { + // Already default initialized to `null`, just being explicit + member_name.put_field_value0( + crate::globals::fields::java_lang_invoke_MemberName::method_field_offset(), + Operand::Reference(Reference::null()), + ); + + let field = calling_class.resolve_field(name, descriptor)?; + + flags = field.access_flags.as_u2() as jint; + flags |= MethodHandleNatives::MN_IS_FIELD; + flags |= (ref_kind as jint) << MethodHandleNatives::MN_REFERENCE_KIND_SHIFT; + + if field.is_trusted_final() { + flags |= MethodHandleNatives::MN_TRUSTED_FINAL; + } + + todo!("MH of kind field"); + }, + ReferenceKind::InvokeVirtual + | ReferenceKind::NewInvokeSpecial + | ReferenceKind::InvokeStatic + | ReferenceKind::InvokeSpecial => { + let method = calling_class.resolve_method(name, descriptor)?; + + flags = method.access_flags.as_u2() as jint; + flags |= MethodHandleNatives::MN_IS_METHOD; + flags |= (ref_kind as jint) << MethodHandleNatives::MN_REFERENCE_KIND_SHIFT; + + match ref_kind { + ReferenceKind::InvokeSpecial => { + is_valid = method.class() == invoking_class + || calling_class + .parent_iter() + .any(|super_class| super_class == invoking_class) + || calling_class + .interfaces + .iter() + .any(|interface| *interface == invoking_class) + || method.class() == crate::globals::classes::java_lang_Object(); + }, + ReferenceKind::NewInvokeSpecial => { + flags |= MethodHandleNatives::MN_IS_CONSTRUCTOR; + + is_valid = method.name == sym!(object_initializer_name); + if method.is_protected() { + is_valid &= method.class().shares_package_with(invoking_class); + } else { + is_valid &= method.class() == calling_class; + } + }, + ReferenceKind::InvokeStatic => { + is_valid = method.is_static(); + }, + ReferenceKind::InvokeVirtual => { + if method.is_protected() && !method.class().shares_package_with(invoking_class) + { + is_valid = method + .class() + .parent_iter() + .any(|super_class| super_class == invoking_class); + } + }, + _ => unreachable!(), + } + + if method.is_caller_sensitive() { + flags |= MethodHandleNatives::MN_CALLER_SENSITIVE; + } + + // Create the java.lang.invoke.ResolvedMethodName instance + let resolved_method_name = + ClassInstance::new(crate::globals::classes::java_lang_invoke_ResolvedMethodName()); + + resolved_method_name.get_mut().put_field_value0( + crate::globals::fields::java_lang_invoke_ResolvedMethodName::vmholder_field_offset( + ), + Operand::Reference(Reference::mirror(method.class().mirror())), + ); + }, + ReferenceKind::InvokeInterface => { + todo!("MH of kind interface method"); + }, + } + + if !is_valid { + throw!(@DEFER IllegalAccessError); + } + + Throws::Ok(()) +} // -- MemberName support -- pub fn init( _env: JniEnv, _class: &'static Class, - self_: Reference, // java.lang.invoke.MemberName - ref_: Reference, // java.lang.Object + _self_: Reference, // java.lang.invoke.MemberName + _ref_: Reference, // java.lang.Object ) { unimplemented!("java.lang.invoke.MethodHandleNatives#init"); } @@ -21,7 +186,7 @@ pub fn init( pub fn expand( _env: JniEnv, _class: &'static Class, - self_: Reference, // java.lang.invoke.MemberName + _self_: Reference, // java.lang.invoke.MemberName ) { unimplemented!("java.lang.invoke.MethodHandleNatives#expand"); } @@ -35,7 +200,59 @@ pub fn resolve( lookup_mode: jint, speculative_resolve: jboolean, ) -> Reference /* java.lang.invoke.MemberName */ { - unimplemented!("java.lang.invoke.MethodHandleNatives#resolve"); + if self_.is_null() { + throw_with_ret!( + Reference::null(), + JavaThread::current(), + NullPointerException + ); + } + + let flags = self_ + .get_field_value0(crate::globals::fields::java_lang_invoke_MemberName::flags_field_offset()) + .expect_int(); + let reference_kind = match ReferenceKind::from_u8((flags >> MN_REFERENCE_KIND_SHIFT) as u8) { + Some(reference_kind) => reference_kind, + None => { + throw_with_ret!( + Reference::null(), + JavaThread::current(), + InternalError, + "obsolete MemberName format" + ); + }, + }; + + match resolve_member_name( + self_.extract_class().get_mut(), + reference_kind, + caller.extract_target_class(), + lookup_mode, + ) { + Throws::Ok(_) => self_.clone(), /* TODO: is this right? `self_` gets modified, should we make a new object and edit that instead? */ + Throws::Exception(exception) => { + if speculative_resolve { + // Speculative resolution is allowed to fail + return Reference::null(); + } + + if reference_kind.is_field() { + throw_with_ret!( + Reference::null(), + JavaThread::current(), + NoSuchFieldError, + "field resolution failed" + ); + } else { + throw_with_ret!( + Reference::null(), + JavaThread::current(), + NoSuchMethodError, + "method resolution failed" + ); + } + }, + } } // -- Field layout queries parallel to jdk.internal.misc.Unsafe -- @@ -43,7 +260,7 @@ pub fn resolve( pub fn objectFieldOffset( _env: JniEnv, _class: &'static Class, - self_: Reference, // java.lang.invoke.MemberName + _self_: Reference, // java.lang.invoke.MemberName ) -> jlong { unimplemented!("java.lang.invoke.MethodHandleNatives#objectFieldOffset"); } @@ -51,7 +268,7 @@ pub fn objectFieldOffset( pub fn staticFieldOffset( _env: JniEnv, _class: &'static Class, - self_: Reference, // java.lang.invoke.MemberName + _self_: Reference, // java.lang.invoke.MemberName ) -> jlong { unimplemented!("java.lang.invoke.MethodHandleNatives#staticFieldOffset"); } @@ -59,7 +276,7 @@ pub fn staticFieldOffset( pub fn staticFieldBase( _env: JniEnv, _class: &'static Class, - self_: Reference, // java.lang.invoke.MemberName + _self_: Reference, // java.lang.invoke.MemberName ) -> Reference /* java.lang.Object */ { unimplemented!("java.lang.invoke.MethodHandleNatives#staticFieldBase"); } @@ -67,7 +284,7 @@ pub fn staticFieldBase( pub fn getMemberVMInfo( _env: JniEnv, _class: &'static Class, - self_: Reference, // java.lang.invoke.MemberName + _self_: Reference, // java.lang.invoke.MemberName ) -> Reference /* java.lang.Object */ { unimplemented!("java.lang.invoke.MethodHandleNatives#getMemberVMInfo"); } @@ -77,8 +294,8 @@ pub fn getMemberVMInfo( pub fn setCallSiteTargetNormal( _env: JniEnv, _class: &'static Class, - site: Reference, // java.lang.invoke.CallSite - target: Reference, // java.lang.invoke.MethodHandle + _site: Reference, // java.lang.invoke.CallSite + _target: Reference, // java.lang.invoke.MethodHandle ) { unimplemented!("java.lang.invoke.MethodHandleNatives#setCallSiteTargetNormal"); } @@ -86,8 +303,8 @@ pub fn setCallSiteTargetNormal( pub fn setCallSiteTargetVolatile( _env: JniEnv, _class: &'static Class, - site: Reference, // java.lang.invoke.CallSite - target: Reference, // java.lang.invoke.MethodHandle + _site: Reference, // java.lang.invoke.CallSite + _target: Reference, // java.lang.invoke.MethodHandle ) { unimplemented!("java.lang.invoke.MethodHandleNatives#setCallSiteTargetVolatile"); } @@ -95,14 +312,14 @@ pub fn setCallSiteTargetVolatile( pub fn copyOutBootstrapArguments( _env: JniEnv, _class: &'static Class, - caller: Reference, // java.lang.Class - index_info: Reference, // int[] - start: jint, - end: jint, - buf: Reference, // java.lang.Object[] - pos: jint, - resolve: jboolean, - if_not_available: Reference, // java.lang.Object + _caller: Reference, // java.lang.Class + _index_info: Reference, // int[] + _start: jint, + _end: jint, + _buf: Reference, // java.lang.Object[] + _pos: jint, + _resolve: jboolean, + _if_not_available: Reference, // java.lang.Object ) { unimplemented!("java.lang.invoke.MethodHandleNatives#copyOutBootstrapArguments"); } @@ -110,7 +327,7 @@ pub fn copyOutBootstrapArguments( pub fn clearCallSiteContext( _env: JniEnv, _class: &'static Class, - context: Reference, // java.lang.invoke.CallSiteContext + _context: Reference, // java.lang.invoke.CallSiteContext ) { unimplemented!("java.lang.invoke.MethodHandleNatives#clearCallSiteContext"); } @@ -118,8 +335,8 @@ pub fn clearCallSiteContext( pub fn getNamedCon( _env: JniEnv, _class: &'static Class, - which: jint, - name: Reference, // java.lang.Object[] + _which: jint, + _name: Reference, // java.lang.Object[] ) -> jint { unimplemented!("java.lang.invoke.MethodHandleNatives#getNamedCon"); } diff --git a/runtime/src/native/java/lang/invoke/def/MethodHandleNatives.def b/runtime/src/native/java/lang/invoke/def/MethodHandleNatives.def index b98f011..f38b41e 100644 --- a/runtime/src/native/java/lang/invoke/def/MethodHandleNatives.def +++ b/runtime/src/native/java/lang/invoke/def/MethodHandleNatives.def @@ -37,5 +37,21 @@ public class MethodHandleNatives { private static native void registerNatives(); + public class Constants { + @Native static final int MN_IS_METHOD = 0x00010000; // method (not constructor) + @Native static final int MN_IS_CONSTRUCTOR = 0x00020000; // constructor + @Native static final int MN_IS_FIELD = 0x00040000; // field + @Native static final int MN_IS_TYPE = 0x00080000; // nested type + @Native static final int MN_CALLER_SENSITIVE = 0x00100000; // @CallerSensitive annotation detected + @Native static final int MN_TRUSTED_FINAL = 0x00200000; // trusted final field + @Native static final int MN_HIDDEN_MEMBER = 0x00400000; // members defined in a hidden class or with @Hidden + @Native static final int MN_REFERENCE_KIND_SHIFT = 24; // refKind + @Native static final int MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT; + + @Native static final int LM_MODULE = 0x10; // Lookup.MODULE + @Native static final int LM_UNCONDITIONAL = 0x20; // Lookup.UNCONDITIONAL + @Native static final int LM_TRUSTED = -1; + } + private static native int getNamedCon(int which, Object[] name); } \ No newline at end of file diff --git a/runtime/src/objects/class/mod.rs b/runtime/src/objects/class/mod.rs index ae29be6..24c4b38 100644 --- a/runtime/src/objects/class/mod.rs +++ b/runtime/src/objects/class/mod.rs @@ -173,6 +173,7 @@ pub enum ClassType { pub struct ClassDescriptor { pub source_file_index: Option, pub constant_pool: ConstantPool, + pub is_record: bool, } impl Debug for ClassDescriptor { @@ -190,6 +191,8 @@ impl Debug for ClassDescriptor { None => debug_struct.field("source_file", &"None"), }; + debug_struct.field("is_record", &self.is_record); + debug_struct.finish() } } @@ -541,6 +544,14 @@ impl Class { self.access_flags.is_abstract() } + /// Whether the class is a record + pub fn is_record(&self) -> bool { + match self.class_ty() { + ClassType::Instance(ref instance) => instance.is_record, + _ => false, + } + } + /// Whether this class is a subclass of `class` pub fn is_subclass_of(&self, class: &Class) -> bool { let mut current_class = self; @@ -638,6 +649,12 @@ impl Class { None => None, }; + // TODO: Actually retain the information from the record attribute + let is_record = parsed_file + .attributes + .iter() + .any(|attr| attr.record().is_some()); + let constant_pool = parsed_file.constant_pool; let name_raw = constant_pool.get::(class_name_index); @@ -693,6 +710,7 @@ impl Class { let class_instance = ClassDescriptor { source_file_index, constant_pool: ConstantPool::new(class, constant_pool), + is_record, }; unsafe { *class.class_ty.get() = MaybeUninit::new(ClassType::Instance(class_instance)); diff --git a/runtime/src/objects/constant_pool/cp_types.rs b/runtime/src/objects/constant_pool/cp_types.rs index 36e8861..2fff917 100644 --- a/runtime/src/objects/constant_pool/cp_types.rs +++ b/runtime/src/objects/constant_pool/cp_types.rs @@ -10,11 +10,15 @@ use crate::string_interner::StringInterner; use crate::thread::exceptions::{throw, Throws}; use crate::thread::JavaThread; +use crate::native::java::lang::invoke::MethodHandleNatives; +use crate::objects::class_instance::ClassInstance; +use crate::objects::instance::Instance; use classfile::constant_pool::types::{raw as raw_types, CpEntry, ReferenceEntry, ReferenceKind}; use classfile::{FieldType, MethodDescriptor}; use common::int_types::{s4, s8, u1, u2}; use common::traits::PtrType; use instructions::Operand; +use jni::sys::jint; use symbols::{sym, Symbol}; /// A constant pool entry of any type @@ -380,6 +384,7 @@ impl EntryType for InvokeDynamic { let bootstrap_method = &bootstrap_methods[value.bootstrap_method_attr_index as usize]; let bsm_handle = cp.get::(bootstrap_method.method_handle_index)?; + panic!("got the bsm"); let link_call_site_method = crate::globals::classes::java_lang_invoke_MethodHandleNatives() .resolve_method(sym!(linkCallSite), sym!(linkCallSite_signature))?; @@ -413,8 +418,7 @@ impl EntryType for MethodHandle { #[inline] fn resolved_entry(entry: ResolvedEntry) -> Self::Resolved { - // unsafe { entry.method_handle } - todo!() + unsafe { entry.method_handle }.clone() } fn resolve( @@ -433,97 +437,75 @@ impl EntryType for MethodHandle { _: u2, value: ::Entry, ) -> Throws { + let callee_class; + let name; + let descriptor; + match value.reference_kind { ReferenceKind::GetField | ReferenceKind::GetStatic | ReferenceKind::PutField | ReferenceKind::PutStatic => { - let ReferenceEntry::FieldRef(field) = value.reference else { - panic!("Expected a field reference"); // TODO: Exception and set failure - }; - - let _class = cp.get::(field.class_index)?; - let (_name, _descriptor) = unsafe { - cp.resolve_entry_with::( - field.name_and_type_index, - field.name_and_type, - )? - }; todo!("MH of kind field"); }, ReferenceKind::InvokeVirtual | ReferenceKind::NewInvokeSpecial | ReferenceKind::InvokeStatic | ReferenceKind::InvokeSpecial => { - let ReferenceEntry::MethodRef(method) = value.reference else { + let ReferenceEntry::MethodRef(method_ref) = value.reference else { panic!("Expected a method reference"); // TODO: Exception and set failure }; - let class = cp.get::(method.class_index)?; - let (name, descriptor) = unsafe { + callee_class = cp.get::(method_ref.class_index)?; + (name, descriptor) = unsafe { cp.resolve_entry_with::( - method.name_and_type_index, - method.name_and_type, + method_ref.name_and_type_index, + method_ref.name_and_type, )? }; - - let method = class.resolve_method(name, descriptor)?; - - let mut is_valid = true; - match value.reference_kind { - ReferenceKind::InvokeSpecial => { - is_valid = method.class() == invoking_class - || class - .parent_iter() - .any(|super_class| super_class == invoking_class) - || class - .interfaces - .iter() - .any(|interface| *interface == invoking_class) - || method.class() == crate::globals::classes::java_lang_Object(); - }, - ReferenceKind::NewInvokeSpecial => { - is_valid = method.name == sym!(object_initializer_name); - if method.is_protected() { - is_valid &= method.class().shares_package_with(invoking_class); - } else { - is_valid &= method.class() == class; - } - }, - ReferenceKind::InvokeStatic => { - is_valid = method.is_static(); - }, - ReferenceKind::InvokeVirtual => { - if method.is_protected() - && !method.class().shares_package_with(invoking_class) - { - is_valid = method - .class() - .parent_iter() - .any(|super_class| super_class == invoking_class); - } - }, - _ => unreachable!(), - } - - if !is_valid { - throw!(@DEFER IllegalAccessError); - } - - if method.is_var_args() { - todo!("varargs method handle"); - } - - todo!("MH of kind method"); }, ReferenceKind::InvokeInterface => { - let ReferenceEntry::MethodRef(method) = value.reference else { - panic!("Expected a method reference"); // TODO: Exception and set failure - }; todo!("MH of kind interface method"); }, } - todo!("Method handle resolution") + + if name == sym!(class_initializer_name) { + throw!(@DEFER IllegalArgumentException, "method handles cannot link to class initializer"); + } + + let member_name = MethodHandleNatives::new_member_name(name, descriptor, callee_class)?; + + MethodHandleNatives::resolve_member_name( + member_name.get_mut(), + value.reference_kind, + invoking_class, + 0, + )?; + + let ty_arg = Method::method_type_for(invoking_class, descriptor.as_str())?; + + let link_method_handle_constant_method = + crate::globals::classes::java_lang_invoke_MethodHandleNatives().resolve_method( + sym!(linkMethodHandleConstant), + sym!(linkMethodHandleConstant_signature), + )?; + + // TODO: Handle throws + let method_handle = java_call!( + JavaThread::current(), + link_method_handle_constant_method, + Operand::Reference(Reference::mirror(invoking_class.mirror())), + Operand::Int(value.reference_kind as i32), + Operand::Reference(Reference::mirror(callee_class.mirror())), + Operand::Reference(Reference::class(StringInterner::intern_symbol(name))), + Operand::Reference(ty_arg), + ) + .expect("method should return something") + .expect_reference(); + + Throws::Ok(ResolvedEntry { + method_handle: Box::leak(Box::new(method_handle)), + }) } } diff --git a/runtime/src/objects/constant_pool/entry.rs b/runtime/src/objects/constant_pool/entry.rs index d461885..69f84f3 100644 --- a/runtime/src/objects/constant_pool/entry.rs +++ b/runtime/src/objects/constant_pool/entry.rs @@ -25,6 +25,7 @@ pub(super) union ResolvedEntry { pub(super) field_ref: &'static Field, pub(super) invoke_dynamic: u32, // TODO pub(super) method_ref: &'static Method, + pub(super) method_handle: &'static Reference, pub(super) string: Symbol, } diff --git a/runtime/src/objects/field.rs b/runtime/src/objects/field.rs index 565b1a5..cda2416 100644 --- a/runtime/src/objects/field.rs +++ b/runtime/src/objects/field.rs @@ -36,17 +36,25 @@ impl Debug for Field { } impl Field { + #[inline] pub fn is_static(&self) -> bool { self.access_flags.is_static() } + #[inline] pub fn is_final(&self) -> bool { self.access_flags.is_final() } + #[inline] pub fn is_volatile(&self) -> bool { self.access_flags.is_volatile() } + + #[inline] + pub fn is_trusted_final(&self) -> bool { + self.is_final() && (self.is_static() || self.class.is_record()) + } } impl Field { diff --git a/runtime/src/objects/method/mod.rs b/runtime/src/objects/method/mod.rs index 466ed19..5a69260 100644 --- a/runtime/src/objects/method/mod.rs +++ b/runtime/src/objects/method/mod.rs @@ -288,7 +288,14 @@ impl Method { FieldType::Void => { panic!("Void parameter"); // TODO: Exception }, - FieldType::Object(_) | FieldType::Array(_) => todo!("Object parameters"), + FieldType::Object(class_name) => { + let class = class.loader().load(Symbol::intern_bytes(&*class_name))?; + parameters.get_mut().store( + index as s4, + Operand::Reference(Reference::mirror(class.mirror())), + )?; + }, + FieldType::Array(_) => todo!("Array parameters"), } } diff --git a/runtime/src/thread/exceptions.rs b/runtime/src/thread/exceptions.rs index 80ba9c6..c78770b 100644 --- a/runtime/src/thread/exceptions.rs +++ b/runtime/src/thread/exceptions.rs @@ -244,7 +244,7 @@ macro_rules! throw { } macro_rules! throw_with_ret { - ($ret:expr, $thread:ident, $($tt:tt)*) => { + ($ret:expr, $thread:expr, $($tt:tt)*) => { let __ex = crate::thread::exceptions::throw!(@CONSTRUCT $($tt)*); __ex.throw(&$thread); return $ret; diff --git a/symbols/src/lib.rs b/symbols/src/lib.rs index 6f1c5d2..0df692f 100644 --- a/symbols/src/lib.rs +++ b/symbols/src/lib.rs @@ -166,6 +166,8 @@ vm_symbols::define_symbols! { java_lang_invoke_MethodHandle: "java/lang/invoke/MethodHandle", java_lang_invoke_MethodHandleNatives: "java/lang/invoke/MethodHandleNatives", java_lang_invoke_VarHandle: "java/lang/invoke/VarHandle", + java_lang_invoke_MemberName: "java/lang/invoke/MemberName", + java_lang_invoke_ResolvedMethodName: "java/lang/invoke/ResolvedMethodName", java_lang_Thread: "java/lang/Thread", java_lang_ThreadGroup: "java/lang/ThreadGroup", java_lang_Thread_FieldHolder: "java/lang/Thread$FieldHolder", @@ -206,6 +208,7 @@ vm_symbols::define_symbols! { ThreadGroup_Runnable_void_signature: "(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;)V", linkCallSite_signature: "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)", findMethodHandleType_signature: "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;", + linkMethodHandleConstant_signature: "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", // -- GENERATED METHOD SIGNATURE MARKER, DO NOT DELETE -- // Types @@ -238,6 +241,7 @@ vm_symbols::define_symbols! { linkCallSite, findMethodHandleType, + linkMethodHandleConstant, initPhase1_name: "initPhase1", initPhase2_name: "initPhase2", @@ -257,37 +261,42 @@ vm_symbols::define_symbols! { BIG_ENDIAN: "BIG_ENDIAN", UNALIGNED_ACCESS: "UNALIGNED_ACCESS", DATA_CACHE_LINE_FLUSH_SIZE: "DATA_CACHE_LINE_FLUSH_SIZE", - name: "name", - module: "module", - classLoader: "classLoader", - referent: "referent", - loader: "loader", - holder: "holder", - eetop: "eetop", - stackSize: "stackSize", - priority: "priority", - daemon: "daemon", - threadStatus: "threadStatus", - value: "value", - coder: "coder", - fd: "fd", - path: "path", + name, + module, + classLoader, + referent, + loader, + holder, + eetop, + stackSize, + priority, + daemon, + threadStatus, + value, + coder, + fd, + path, r#in: "in", - out: "out", - err: "err", - stackTrace: "stackTrace", - backtrace: "backtrace", - depth: "depth", - declaringClassObject: "declaringClassObject", - classLoaderName: "classLoaderName", - moduleName: "moduleName", - moduleVersion: "moduleVersion", - declaringClass: "declaringClass", - methodName: "methodName", - fileName: "fileName", - lineNumber: "lineNumber", - unnamedModule: "unnamedModule", - nameAndId: "nameAndId", + out, + err, + stackTrace, + backtrace, + depth, + declaringClassObject, + classLoaderName, + moduleName, + moduleVersion, + declaringClass, + methodName, + fileName, + lineNumber, + unnamedModule, + nameAndId, + r#type: "type", + flags, + clazz, + method, + vmholder, // Injected fields loader_ptr,