rb_sys/
stable_api.rs

1//! Stable ABI functions which provide access to Ruby internals that
2//! is compatible across Ruby versions, and are guaranteed to be not break due
3//! to Ruby binary changes.
4//!
5//! ### Goals
6//!
7//! 1. To provide access to Ruby internals that are not exposed by the libruby
8//!    (i.e. C macros and inline functions).
9//! 2. Provide support for Ruby development versions, which can make breaking
10//!    changes without semantic versioning. We want to support these versions
11//!    to ensure Rust extensions don't prevent the Ruby core team from testing
12//!    changes in production.
13
14use crate::{ID, VALUE};
15use std::{
16    os::raw::{c_char, c_long},
17    ptr::NonNull,
18    time::Duration,
19};
20
21pub trait StableApiDefinition {
22    const VERSION_MAJOR: u32;
23    const VERSION_MINOR: u32;
24
25    fn version(&self) -> (u32, u32) {
26        (Self::VERSION_MAJOR, Self::VERSION_MINOR)
27    }
28
29    /// Get the length of a Ruby string (akin to `RSTRING_LEN`).
30    ///
31    /// # Safety
32    /// This function is unsafe because it dereferences a raw pointer to get
33    /// access to underlying Ruby data. The caller must ensure that the pointer
34    /// is valid.
35    unsafe fn rstring_len(&self, obj: VALUE) -> c_long;
36
37    /// Get a pointer to the bytes of a Ruby string (akin to `RSTRING_PTR`).
38    ///
39    /// # Safety
40    /// This function is unsafe because it dereferences a raw pointer to get
41    /// access to underlying Ruby data. The caller must ensure that the pointer
42    /// is valid.
43    unsafe fn rstring_ptr(&self, obj: VALUE) -> *const c_char;
44
45    /// Get the length of a Ruby array (akin to `RARRAY_LEN`).
46    ///
47    /// # Safety
48    /// This function is unsafe because it dereferences a raw pointer to get
49    /// access to underlying Ruby data. The caller must ensure that the pointer
50    /// is valid.
51    unsafe fn rarray_len(&self, obj: VALUE) -> c_long;
52
53    /// Get a pointer to the elements of a Ruby array (akin to `RARRAY_CONST_PTR`).
54    ///
55    /// # Safety
56    /// This function is unsafe because it dereferences a raw pointer to get
57    /// access to underlying Ruby data. The caller must ensure that the pointer
58    /// is valid.
59    unsafe fn rarray_const_ptr(&self, obj: VALUE) -> *const VALUE;
60
61    /// Get the class from a VALUE which contains an RBasic struct.
62    ///
63    /// `VALUE` is a valid pointer to a non-immediate object.
64    ///
65    /// # Safety
66    /// This function is unsafe because it dereferences a raw pointer to get
67    /// access to underlying RBasic struct. The caller must ensure that the
68    /// `VALUE` is a valid pointer to an RBasic struct.
69    unsafe fn rbasic_class(&self, obj: VALUE) -> Option<NonNull<VALUE>>;
70
71    /// Checks if the given object is frozen.
72    ///
73    /// `VALUE` is a valid pointer to a non-immediate object.
74    ///
75    /// # Safety
76    /// This function is unsafe because it may dereference a raw pointer to get
77    /// access to underlying RBasic struct. The caller must ensure that the
78    /// `VALUE` is a valid pointer to an RBasic struct.
79    unsafe fn frozen_p(&self, obj: VALUE) -> bool;
80
81    /// Tests if a bignum is positive.
82    ///
83    /// # Safety
84    /// This function is unsafe because it dereferences a raw pointer to get
85    /// access to underlying RBasic struct. The caller must ensure that the
86    /// `VALUE` is a valid pointer to a bignum.
87    unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool;
88
89    /// Tests if a bignum is negative.
90    ///
91    /// # Safety
92    /// This function is unsafe because it dereferences a raw pointer to get
93    /// access to underlying RBasic struct. The caller must ensure that the
94    /// `VALUE` is a valid pointer to a bignum.
95    #[inline]
96    unsafe fn bignum_negative_p(&self, obj: VALUE) -> bool {
97        !self.bignum_positive_p(obj)
98    }
99
100    /// Tests if the given value is a special constant.
101    fn special_const_p(&self, value: VALUE) -> bool;
102
103    /// Queries the type of the object.
104    ///
105    /// # Note
106    /// The input `obj` must not be a special constant.
107    ///
108    /// # Safety
109    /// This function is unsafe because it could dereference a raw pointer when
110    /// attemping to access the underlying [`RBasic`] struct.
111    unsafe fn builtin_type(&self, obj: VALUE) -> crate::ruby_value_type;
112
113    /// Tests if the object's type is the given type.
114    ///
115    /// # Safety
116    /// This function is unsafe because it could dereference a raw pointer when
117    /// attemping to access the underlying [`RBasic`] struct.
118    unsafe fn type_p(&self, obj: VALUE, ty: crate::ruby_value_type) -> bool;
119
120    /// Checks if the given object is nil.
121    fn nil_p(&self, obj: VALUE) -> bool;
122
123    /// Checks if the given object is a so-called Fixnum.
124    fn fixnum_p(&self, obj: VALUE) -> bool;
125
126    /// Checks if the given object is a dynamic symbol.
127    ///
128    /// # Safety
129    /// This function is unsafe because it could dereference a raw pointer when
130    /// attemping to access the underlying [`RBasic`] struct.
131    unsafe fn dynamic_sym_p(&self, obj: VALUE) -> bool;
132
133    /// Checks if the given object is a static symbol.
134    fn static_sym_p(&self, obj: VALUE) -> bool;
135
136    /// Checks if the given object is a symbol.
137    ///
138    /// # Safety
139    /// This function is unsafe because it could dereference a raw pointer when
140    /// attemping to access the underlying [`RBasic`] struct.
141    unsafe fn symbol_p(&self, obj: VALUE) -> bool;
142
143    /// Checks if the given object is a so-called Flonum.
144    ///
145    /// # Safety
146    /// This function is unsafe because it could dereference a raw pointer when
147    /// attemping to access the underlying [`RBasic`] struct.
148    unsafe fn float_type_p(&self, obj: VALUE) -> bool;
149
150    /// Checks if the given object is an integer type
151    ///
152    /// # Safety
153    /// This function is unsafe because it could dereference a raw pointer when
154    /// attemping to access the underlying [`RBasic`] struct.
155    unsafe fn integer_type_p(&self, obj: VALUE) -> bool;
156
157    /// Checks if the given object is a so-called Flonum.
158    fn flonum_p(&self, obj: VALUE) -> bool;
159
160    /// Checks if the given  object is  an immediate  i.e. an  object which  has
161    /// no corresponding storage inside of the object space.
162    fn immediate_p(&self, obj: VALUE) -> bool;
163
164    /// Emulates Ruby's "if" statement by testing if the given `obj` is neither `Qnil` or `Qfalse`.
165    ///
166    /// # Safety
167    /// This function is unsafe because it could dereference a raw pointer when
168    /// attemping to access the underlying [`RBasic`] struct.
169    fn rb_test(&self, ob: VALUE) -> bool;
170
171    /// Queries the type of the object. Identical to `StableApi.builtin_type`,
172    /// except it can also accept special constants.
173    ///
174    /// # Safety
175    /// This function is unsafe because it could dereference a raw pointer when
176    /// attemping to access the underlying [`RBasic`] struct.
177    unsafe fn rb_type(&self, obj: VALUE) -> crate::ruby_value_type;
178
179    /// Check if a Ruby string is interned (akin to `RSTRING_FSTR`).
180    ///
181    /// # Safety
182    /// This function is unsafe because it dereferences a raw pointer to get
183    /// access to underlying flags of the RString. The caller must ensure that
184    /// the `VALUE` is a valid pointer to an RString.
185    unsafe fn rstring_interned_p(&self, obj: VALUE) -> bool;
186
187    /// Blocks the current thread until the given duration has passed.
188    fn thread_sleep(&self, duration: Duration);
189
190    /// Checks if the given object is an RTypedData.
191    ///
192    /// # Safety
193    /// This function is unsafe because it dereferences a raw pointer to get
194    /// access to underlying Ruby data. The caller must ensure that the pointer
195    /// is valid and points to a T_DATA object.
196    unsafe fn rtypeddata_p(&self, obj: VALUE) -> bool;
197
198    /// Checks if the given RTypedData is embedded.
199    ///
200    /// # Safety
201    /// This function is unsafe because it dereferences a raw pointer to get
202    /// access to underlying Ruby data. The caller must ensure that the pointer
203    /// is valid and points to an RTypedData object.
204    unsafe fn rtypeddata_embedded_p(&self, obj: VALUE) -> bool;
205
206    /// Gets the data type from an RTypedData object.
207    ///
208    /// # Safety
209    /// This function is unsafe because it dereferences a raw pointer to get
210    /// access to underlying Ruby data. The caller must ensure that the pointer
211    /// is valid and points to an RTypedData object.
212    unsafe fn rtypeddata_type(&self, obj: VALUE) -> *const crate::rb_data_type_t;
213
214    /// Gets the data pointer from an RTypedData object.
215    ///
216    /// # Safety
217    /// This function is unsafe because it dereferences a raw pointer to get
218    /// access to underlying Ruby data. The caller must ensure that the pointer
219    /// is valid and points to an RTypedData object.
220    unsafe fn rtypeddata_get_data(&self, obj: VALUE) -> *mut std::ffi::c_void;
221
222    /// Convert Fixnum to long (akin to `FIX2LONG`).
223    ///
224    /// Extracts the integer value from a Fixnum VALUE.
225    ///
226    /// # Safety assumptions
227    /// - `obj` must be a valid Fixnum VALUE
228    fn fix2long(&self, obj: VALUE) -> std::os::raw::c_long;
229
230    /// Convert Fixnum to unsigned long (akin to `FIX2ULONG`).
231    ///
232    /// Extracts the unsigned integer value from a Fixnum VALUE.
233    ///
234    /// # Safety assumptions
235    /// - `obj` must be a valid positive Fixnum VALUE
236    fn fix2ulong(&self, obj: VALUE) -> std::os::raw::c_ulong;
237
238    /// Convert long to Fixnum (akin to `LONG2FIX`).
239    ///
240    /// Creates a Fixnum VALUE from a long integer.
241    ///
242    /// # Safety assumptions
243    /// - `val` must be in the valid Fixnum range
244    fn long2fix(&self, val: std::os::raw::c_long) -> VALUE;
245
246    /// Check if long value can be represented as Fixnum (akin to `FIXABLE`).
247    ///
248    /// Returns true if the value fits in a Fixnum.
249    fn fixable(&self, val: std::os::raw::c_long) -> bool;
250
251    /// Check if unsigned long value can be represented as positive Fixnum (akin to `POSFIXABLE`).
252    ///
253    /// Returns true if the value fits in a positive Fixnum.
254    fn posfixable(&self, val: std::os::raw::c_ulong) -> bool;
255
256    /// Convert Ruby Integer to long (akin to `NUM2LONG`).
257    ///
258    /// Converts any Ruby Integer (Fixnum or Bignum) to a C long.
259    /// May raise an exception if the value is out of range.
260    ///
261    /// # Safety
262    /// - `obj` must be a valid Integer VALUE
263    /// - May call into Ruby runtime (for Bignum conversion)
264    unsafe fn num2long(&self, obj: VALUE) -> std::os::raw::c_long;
265
266    /// Convert Ruby Integer to unsigned long (akin to `NUM2ULONG`).
267    ///
268    /// Converts any Ruby Integer (Fixnum or Bignum) to a C unsigned long.
269    /// May raise an exception if the value is out of range or negative.
270    ///
271    /// # Safety
272    /// - `obj` must be a valid Integer VALUE
273    /// - May call into Ruby runtime (for Bignum conversion)
274    unsafe fn num2ulong(&self, obj: VALUE) -> std::os::raw::c_ulong;
275
276    /// Convert long to Ruby Integer (akin to `LONG2NUM`).
277    ///
278    /// Creates a Ruby Integer (Fixnum or Bignum) from a C long.
279    /// Uses Fixnum if possible, otherwise allocates a Bignum.
280    fn long2num(&self, val: std::os::raw::c_long) -> VALUE;
281
282    /// Convert unsigned long to Ruby Integer (akin to `ULONG2NUM`).
283    ///
284    /// Creates a Ruby Integer (Fixnum or Bignum) from a C unsigned long.
285    /// Uses Fixnum if possible, otherwise allocates a Bignum.
286    fn ulong2num(&self, val: std::os::raw::c_ulong) -> VALUE;
287
288    /// Convert ID to Symbol (akin to `RB_ID2SYM`).
289    ///
290    /// Converts an internal ID to its corresponding Symbol VALUE.
291    /// This is a safe operation - just bit manipulation for static symbols.
292    fn id2sym(&self, id: ID) -> VALUE;
293
294    /// Convert Symbol to ID (akin to `RB_SYM2ID`).
295    ///
296    /// Converts a Symbol VALUE to its internal ID representation.
297    ///
298    /// # Safety
299    /// - `obj` must be a valid Symbol VALUE
300    /// - For dynamic symbols, this may access the heap
301    unsafe fn sym2id(&self, obj: VALUE) -> ID;
302}
303
304#[cfg(stable_api_enable_compiled_mod)]
305mod compiled;
306#[cfg(stable_api_export_compiled_as_api)]
307use compiled as api;
308
309#[cfg(stable_api_include_rust_impl)]
310#[cfg_attr(ruby_eq_2_7, path = "stable_api/ruby_2_7.rs")]
311#[cfg_attr(ruby_eq_3_0, path = "stable_api/ruby_3_0.rs")]
312#[cfg_attr(ruby_eq_3_1, path = "stable_api/ruby_3_1.rs")]
313#[cfg_attr(ruby_eq_3_2, path = "stable_api/ruby_3_2.rs")]
314#[cfg_attr(ruby_eq_3_3, path = "stable_api/ruby_3_3.rs")]
315#[cfg_attr(ruby_eq_3_4, path = "stable_api/ruby_3_4.rs")]
316#[cfg_attr(ruby_eq_4_0, path = "stable_api/ruby_4_0.rs")]
317mod rust;
318#[cfg(not(stable_api_export_compiled_as_api))]
319use rust as api;
320
321impl std::fmt::Debug for api::Definition {
322    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323        f.debug_struct("StableApiDefinition")
324            .field("VERSION_MAJOR", &api::Definition::VERSION_MAJOR)
325            .field("VERSION_MINOR", &api::Definition::VERSION_MINOR)
326            .finish()
327    }
328}
329
330/// Get the default stable API definition for the current Ruby version.
331#[inline(always)]
332pub const fn get_default() -> &'static api::Definition {
333    const API: api::Definition = api::Definition {};
334    &API
335}
336
337/// Get the fallback stable API definition for the current Ruby version, which
338/// is compiled C code that is linked into to this crate.
339#[inline(always)]
340#[cfg(stable_api_enable_compiled_mod)]
341pub const fn get_compiled() -> &'static compiled::Definition {
342    const COMPILED_API: compiled::Definition = compiled::Definition {};
343    &COMPILED_API
344}