diff --git a/crates/rb-sys-tests/src/stable_api_test.rs b/crates/rb-sys-tests/src/stable_api_test.rs index f639f88e..4bfbef85 100644 --- a/crates/rb-sys-tests/src/stable_api_test.rs +++ b/crates/rb-sys-tests/src/stable_api_test.rs @@ -1,4 +1,4 @@ -use rb_sys::StableApiDefinition; +use rb_sys::{StableApiDefinition, VALUE}; use rb_sys_test_helpers::rstring as gen_rstring; macro_rules! parity_test { @@ -181,6 +181,46 @@ parity_test!( } ); +parity_test!( + name: test_rbasic_class_of_array, + func: rbasic_class, + data_factory: { + unsafe { rb_sys::rb_ary_new() as VALUE } + }, + expected: { + unsafe { Some(std::ptr::NonNull::new_unchecked(rb_sys::rb_cArray as _)) } + } +); + +parity_test!( + name: test_rbasic_class_of_array_evaled, + func: rbasic_class, + data_factory: { + ruby_eval!("[]") + }, + expected: { + unsafe { Some(std::ptr::NonNull::new_unchecked(ruby_eval!("Array") as *mut VALUE)) } + } +); + +parity_test!( + name: test_rbasic_frozen_p_not_frozen_obj, + func: frozen_p, + data_factory: { + ruby_eval!("[1]") + }, + expected: false +); + +parity_test!( + name: test_rbasic_frozen_p_frozen_obj, + func: frozen_p, + data_factory: { + ruby_eval!("[1].freeze") + }, + expected: true +); + parity_test!( name: test_special_const_p_for_bool, func: special_const_p, diff --git a/crates/rb-sys/src/stable_api.rs b/crates/rb-sys/src/stable_api.rs index fe3b0cb8..424e578c 100644 --- a/crates/rb-sys/src/stable_api.rs +++ b/crates/rb-sys/src/stable_api.rs @@ -12,7 +12,10 @@ //! changes in production. use crate::VALUE; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; pub trait StableApiDefinition { const VERSION_MAJOR: u32; @@ -54,6 +57,16 @@ pub trait StableApiDefinition { /// is valid. unsafe fn rarray_const_ptr(&self, obj: VALUE) -> *const VALUE; + /// Get the class from a VALUE which contains an RBasic struct. + /// + /// `VALUE` is a valid pointer to a non-immediate object. + unsafe fn rbasic_class(&self, obj: VALUE) -> Option>; + + /// Checks if the given object is frozen. + /// + /// `VALUE` is a valid pointer to a non-immediate object. + unsafe fn frozen_p(&self, obj: VALUE) -> bool; + /// Tests if a bignum is positive. /// /// # Safety diff --git a/crates/rb-sys/src/stable_api/compiled.c b/crates/rb-sys/src/stable_api/compiled.c index 739f6634..3d5cc134 100644 --- a/crates/rb-sys/src/stable_api/compiled.c +++ b/crates/rb-sys/src/stable_api/compiled.c @@ -20,6 +20,16 @@ impl_rarray_const_ptr(VALUE obj) { return RARRAY_CONST_PTR(obj); } +VALUE +impl_rbasic_class(VALUE obj) { + return RBASIC_CLASS(obj); +} + +int +impl_frozen_p(VALUE obj) { + return RB_OBJ_FROZEN(obj); +} + int impl_special_const_p(VALUE obj) { return SPECIAL_CONST_P(obj); diff --git a/crates/rb-sys/src/stable_api/compiled.rs b/crates/rb-sys/src/stable_api/compiled.rs index c142e7c2..44284b1b 100644 --- a/crates/rb-sys/src/stable_api/compiled.rs +++ b/crates/rb-sys/src/stable_api/compiled.rs @@ -1,6 +1,9 @@ use super::StableApiDefinition; use crate::{ruby_value_type, VALUE}; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[allow(dead_code)] extern "C" { @@ -16,6 +19,12 @@ extern "C" { #[link_name = "impl_rarray_const_ptr"] fn impl_rarray_const_ptr(ary: VALUE) -> *const VALUE; + #[link_name = "impl_rbasic_class"] + fn impl_rbasic_class(obj: VALUE) -> VALUE; + + #[link_name = "impl_frozen_p"] + fn impl_frozen_p(obj: VALUE) -> bool; + #[link_name = "impl_special_const_p"] fn impl_special_const_p(value: VALUE) -> bool; @@ -94,6 +103,15 @@ impl StableApiDefinition for Definition { impl_rarray_const_ptr(obj) } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + NonNull::::new(impl_rbasic_class(obj) as _) + } + + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + impl_frozen_p(obj) + } + #[inline] fn special_const_p(&self, value: VALUE) -> bool { unsafe { impl_special_const_p(value) } diff --git a/crates/rb-sys/src/stable_api/ruby_2_6.rs b/crates/rb-sys/src/stable_api/ruby_2_6.rs index a36e85be..4e39effe 100644 --- a/crates/rb-sys/src/stable_api/ruby_2_6.rs +++ b/crates/rb-sys/src/stable_api/ruby_2_6.rs @@ -6,7 +6,10 @@ use crate::{ internal::{RArray, RString}, value_type, VALUE, }; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[cfg(not(ruby_eq_2_6))] compile_error!("This file should only be included in Ruby 2.6 builds"); @@ -88,6 +91,23 @@ impl StableApiDefinition for Definition { ptr } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + let rbasic = obj as *const crate::RBasic; + + NonNull::::new((*rbasic).klass as _) + } + + #[inline] + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + if self.special_const_p(obj) { + true + } else { + let rbasic = obj as *const crate::Rbasic; + ((*rbasic).flags & crate::ruby_fl_type::RUBY_FL_FREEZE as VALUE) != 0 + } + } + #[inline] unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool { let rbasic = obj as *const crate::RBasic; diff --git a/crates/rb-sys/src/stable_api/ruby_2_7.rs b/crates/rb-sys/src/stable_api/ruby_2_7.rs index f228c7c3..3da2e517 100644 --- a/crates/rb-sys/src/stable_api/ruby_2_7.rs +++ b/crates/rb-sys/src/stable_api/ruby_2_7.rs @@ -3,7 +3,10 @@ use crate::{ internal::{RArray, RString}, value_type, VALUE, }; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[cfg(not(ruby_eq_2_7))] compile_error!("This file should only be included in Ruby 2.7 builds"); @@ -88,6 +91,23 @@ impl StableApiDefinition for Definition { ptr } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + let rbasic = obj as *const crate::RBasic; + + NonNull::::new((*rbasic).klass as _) + } + + #[inline] + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + if self.special_const_p(obj) { + true + } else { + let rbasic = obj as *const crate::RBasic; + ((*rbasic).flags & crate::ruby_fl_type::RUBY_FL_FREEZE as VALUE) != 0 + } + } + #[inline] unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool { let rbasic = obj as *const crate::RBasic; diff --git a/crates/rb-sys/src/stable_api/ruby_3_0.rs b/crates/rb-sys/src/stable_api/ruby_3_0.rs index cc059f91..51b9792f 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_0.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_0.rs @@ -3,7 +3,10 @@ use crate::{ internal::{RArray, RString}, value_type, VALUE, }; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[cfg(not(ruby_eq_3_0))] compile_error!("This file should only be included in Ruby 3.0 builds"); @@ -96,6 +99,23 @@ impl StableApiDefinition for Definition { } } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + let rbasic = obj as *const crate::RBasic; + + NonNull::::new((*rbasic).klass as _) + } + + #[inline] + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + if self.special_const_p(obj) { + true + } else { + let rbasic = obj as *const crate::RBasic; + ((*rbasic).flags & crate::ruby_fl_type::RUBY_FL_FREEZE as VALUE) != 0 + } + } + #[inline] unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool { let rbasic = obj as *const crate::RBasic; diff --git a/crates/rb-sys/src/stable_api/ruby_3_1.rs b/crates/rb-sys/src/stable_api/ruby_3_1.rs index e6b747e0..0124db70 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_1.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_1.rs @@ -3,7 +3,10 @@ use crate::{ internal::{RArray, RString}, value_type, VALUE, }; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[cfg(not(ruby_eq_3_1))] compile_error!("This file should only be included in Ruby 3.1 builds"); @@ -89,6 +92,23 @@ impl StableApiDefinition for Definition { ret } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + let rbasic = obj as *const crate::RBasic; + + NonNull::::new((*rbasic).klass as _) + } + + #[inline] + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + if self.special_const_p(obj) { + true + } else { + let rbasic = obj as *const crate::RBasic; + ((*rbasic).flags & crate::ruby_fl_type::RUBY_FL_FREEZE as VALUE) != 0 + } + } + #[inline] unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool { let rbasic = obj as *const crate::RBasic; diff --git a/crates/rb-sys/src/stable_api/ruby_3_2.rs b/crates/rb-sys/src/stable_api/ruby_3_2.rs index c6f66e5f..2ab62269 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_2.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_2.rs @@ -3,7 +3,10 @@ use crate::{ internal::{RArray, RString}, value_type, VALUE, }; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[cfg(not(ruby_eq_3_2))] compile_error!("This file should only be included in Ruby 3.2 builds"); @@ -83,6 +86,23 @@ impl StableApiDefinition for Definition { ptr } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + let rbasic = obj as *const crate::RBasic; + + NonNull::::new((*rbasic).klass as _) + } + + #[inline] + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + if self.special_const_p(obj) { + true + } else { + let rbasic = obj as *const crate::RBasic; + ((*rbasic).flags & crate::ruby_fl_type::RUBY_FL_FREEZE as VALUE) != 0 + } + } + #[inline] unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool { let rbasic = obj as *const crate::RBasic; diff --git a/crates/rb-sys/src/stable_api/ruby_3_3.rs b/crates/rb-sys/src/stable_api/ruby_3_3.rs index d4bd09fa..fb05f8e3 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_3.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_3.rs @@ -3,7 +3,10 @@ use crate::{ internal::{RArray, RString}, value_type, VALUE, }; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[cfg(not(ruby_eq_3_3))] compile_error!("This file should only be included in Ruby 3.3 builds"); @@ -76,6 +79,23 @@ impl StableApiDefinition for Definition { ptr } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + let rbasic = obj as *const crate::RBasic; + + NonNull::::new((*rbasic).klass as _) + } + + #[inline] + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + if self.special_const_p(obj) { + true + } else { + let rbasic = obj as *const crate::RBasic; + ((*rbasic).flags & crate::ruby_fl_type::RUBY_FL_FREEZE as VALUE) != 0 + } + } + #[inline] unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool { let rbasic = obj as *const crate::RBasic; diff --git a/crates/rb-sys/src/stable_api/ruby_3_4.rs b/crates/rb-sys/src/stable_api/ruby_3_4.rs index a4a9f7dc..3f370a3d 100644 --- a/crates/rb-sys/src/stable_api/ruby_3_4.rs +++ b/crates/rb-sys/src/stable_api/ruby_3_4.rs @@ -3,7 +3,10 @@ use crate::{ internal::{RArray, RString}, value_type, VALUE, }; -use std::os::raw::{c_char, c_long}; +use std::{ + os::raw::{c_char, c_long}, + ptr::NonNull, +}; #[cfg(not(ruby_eq_3_4))] compile_error!("This file should only be included in Ruby 3.3 builds"); @@ -76,6 +79,23 @@ impl StableApiDefinition for Definition { ptr } + #[inline] + unsafe fn rbasic_class(&self, obj: VALUE) -> Option> { + let rbasic = obj as *const crate::RBasic; + + NonNull::::new((*rbasic).klass as _) + } + + #[inline] + unsafe fn frozen_p(&self, obj: VALUE) -> bool { + if self.special_const_p(obj) { + true + } else { + let rbasic = obj as *const crate::RBasic; + ((*rbasic).flags & crate::ruby_fl_type::RUBY_FL_FREEZE as VALUE) != 0 + } + } + #[inline] fn special_const_p(&self, value: VALUE) -> bool { let is_immediate = (value) & (crate::special_consts::IMMEDIATE_MASK as VALUE) != 0;