Skip to main content

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 element from array by index (akin to `RARRAY_AREF`).
62    ///
63    /// # Safety
64    /// This function is unsafe because it dereferences a raw pointer to get
65    /// access to underlying Ruby data. The caller must ensure that the pointer
66    /// is valid and that the index is within bounds.
67    unsafe fn rarray_aref(&self, obj: VALUE, idx: isize) -> VALUE;
68
69    /// Set element in array by index (akin to `RARRAY_ASET`).
70    ///
71    /// Includes GC write barrier for safety.
72    ///
73    /// # Safety
74    /// This function is unsafe because it dereferences a raw pointer to get
75    /// access to underlying Ruby data. The caller must ensure that the pointer
76    /// is valid and that the index is within bounds.
77    unsafe fn rarray_aset(&self, obj: VALUE, idx: isize, val: VALUE);
78
79    /// Get the class from a VALUE which contains an RBasic struct.
80    ///
81    /// `VALUE` is a valid pointer to a non-immediate object.
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 an RBasic struct.
87    unsafe fn rbasic_class(&self, obj: VALUE) -> Option<NonNull<VALUE>>;
88
89    /// Checks if the given object is frozen.
90    ///
91    /// `VALUE` is a valid pointer to a non-immediate object.
92    ///
93    /// # Safety
94    /// This function is unsafe because it may dereference a raw pointer to get
95    /// access to underlying RBasic struct. The caller must ensure that the
96    /// `VALUE` is a valid pointer to an RBasic struct.
97    unsafe fn frozen_p(&self, obj: VALUE) -> bool;
98
99    /// Tests if a bignum is positive.
100    ///
101    /// # Safety
102    /// This function is unsafe because it dereferences a raw pointer to get
103    /// access to underlying RBasic struct. The caller must ensure that the
104    /// `VALUE` is a valid pointer to a bignum.
105    unsafe fn bignum_positive_p(&self, obj: VALUE) -> bool;
106
107    /// Tests if a bignum is negative.
108    ///
109    /// # Safety
110    /// This function is unsafe because it dereferences a raw pointer to get
111    /// access to underlying RBasic struct. The caller must ensure that the
112    /// `VALUE` is a valid pointer to a bignum.
113    #[inline]
114    unsafe fn bignum_negative_p(&self, obj: VALUE) -> bool {
115        !self.bignum_positive_p(obj)
116    }
117
118    /// Tests if the given value is a special constant.
119    fn special_const_p(&self, value: VALUE) -> bool;
120
121    /// Queries the type of the object.
122    ///
123    /// # Note
124    /// The input `obj` must not be a special constant.
125    ///
126    /// # Safety
127    /// This function is unsafe because it could dereference a raw pointer when
128    /// attemping to access the underlying [`RBasic`] struct.
129    unsafe fn builtin_type(&self, obj: VALUE) -> crate::ruby_value_type;
130
131    /// Tests if the object's type is the given type.
132    ///
133    /// # Safety
134    /// This function is unsafe because it could dereference a raw pointer when
135    /// attemping to access the underlying [`RBasic`] struct.
136    unsafe fn type_p(&self, obj: VALUE, ty: crate::ruby_value_type) -> bool;
137
138    /// Checks if the given object is nil.
139    fn nil_p(&self, obj: VALUE) -> bool;
140
141    /// Checks if the given object is a so-called Fixnum.
142    fn fixnum_p(&self, obj: VALUE) -> bool;
143
144    /// Checks if the given object is a dynamic symbol.
145    ///
146    /// # Safety
147    /// This function is unsafe because it could dereference a raw pointer when
148    /// attemping to access the underlying [`RBasic`] struct.
149    unsafe fn dynamic_sym_p(&self, obj: VALUE) -> bool;
150
151    /// Checks if the given object is a static symbol.
152    fn static_sym_p(&self, obj: VALUE) -> bool;
153
154    /// Checks if the given object is a symbol.
155    ///
156    /// # Safety
157    /// This function is unsafe because it could dereference a raw pointer when
158    /// attemping to access the underlying [`RBasic`] struct.
159    unsafe fn symbol_p(&self, obj: VALUE) -> bool;
160
161    /// Checks if the given object is a so-called Flonum.
162    ///
163    /// # Safety
164    /// This function is unsafe because it could dereference a raw pointer when
165    /// attemping to access the underlying [`RBasic`] struct.
166    unsafe fn float_type_p(&self, obj: VALUE) -> bool;
167
168    /// Checks if the given object is an integer type
169    ///
170    /// # Safety
171    /// This function is unsafe because it could dereference a raw pointer when
172    /// attemping to access the underlying [`RBasic`] struct.
173    unsafe fn integer_type_p(&self, obj: VALUE) -> bool;
174
175    /// Checks if the given object is a so-called Flonum.
176    fn flonum_p(&self, obj: VALUE) -> bool;
177
178    /// Checks if the given  object is  an immediate  i.e. an  object which  has
179    /// no corresponding storage inside of the object space.
180    fn immediate_p(&self, obj: VALUE) -> bool;
181
182    /// Emulates Ruby's "if" statement by testing if the given `obj` is neither `Qnil` or `Qfalse`.
183    ///
184    /// # Safety
185    /// This function is unsafe because it could dereference a raw pointer when
186    /// attemping to access the underlying [`RBasic`] struct.
187    fn rb_test(&self, ob: VALUE) -> bool;
188
189    /// Queries the type of the object. Identical to `StableApi.builtin_type`,
190    /// except it can also accept special constants.
191    ///
192    /// # Safety
193    /// This function is unsafe because it could dereference a raw pointer when
194    /// attemping to access the underlying [`RBasic`] struct.
195    unsafe fn rb_type(&self, obj: VALUE) -> crate::ruby_value_type;
196
197    /// Check if a Ruby string is interned (akin to `RSTRING_FSTR`).
198    ///
199    /// # Safety
200    /// This function is unsafe because it dereferences a raw pointer to get
201    /// access to underlying flags of the RString. The caller must ensure that
202    /// the `VALUE` is a valid pointer to an RString.
203    unsafe fn rstring_interned_p(&self, obj: VALUE) -> bool;
204
205    /// Blocks the current thread until the given duration has passed.
206    fn thread_sleep(&self, duration: Duration);
207
208    /// Checks if the given object is an RTypedData.
209    ///
210    /// # Safety
211    /// This function is unsafe because it dereferences a raw pointer to get
212    /// access to underlying Ruby data. The caller must ensure that the pointer
213    /// is valid and points to a T_DATA object.
214    unsafe fn rtypeddata_p(&self, obj: VALUE) -> bool;
215
216    /// Gets the data type from an RTypedData object.
217    ///
218    /// # Safety
219    /// This function is unsafe because it dereferences a raw pointer to get
220    /// access to underlying Ruby data. The caller must ensure that the pointer
221    /// is valid and points to an RTypedData object.
222    unsafe fn rtypeddata_type(&self, obj: VALUE) -> *const crate::rb_data_type_t;
223
224    /// Gets the data pointer from an RTypedData object.
225    ///
226    /// # Safety
227    /// This function is unsafe because it dereferences a raw pointer to get
228    /// access to underlying Ruby data. The caller must ensure that the pointer
229    /// is valid and points to an RTypedData object.
230    unsafe fn rtypeddata_get_data(&self, obj: VALUE) -> *mut std::ffi::c_void;
231
232    /// Get pointer to end of string contents (akin to `RSTRING_END`).
233    ///
234    /// Returns a pointer to the byte after the last character of the string.
235    ///
236    /// # Safety
237    /// - `obj` must be a valid Ruby String object
238    unsafe fn rstring_end(&self, obj: VALUE) -> *const c_char;
239
240    /// Get data pointer from RData/TypedData object (akin to `DATA_PTR`).
241    ///
242    /// Returns the user data pointer stored in an RData or TypedData object.
243    ///
244    /// # Safety
245    /// - `obj` must be a valid RData or TypedData object
246    unsafe fn rdata_ptr(&self, obj: VALUE) -> *mut std::ffi::c_void;
247
248    /// Freeze an object (akin to `RB_OBJ_FREEZE`).
249    ///
250    /// Sets the frozen flag on an object, preventing further modification.
251    ///
252    /// # Safety
253    /// - `obj` must be a valid heap-allocated Ruby object
254    unsafe fn rb_obj_freeze(&self, obj: VALUE);
255
256    /// Check if object is promoted to old GC generation (akin to `RB_OBJ_PROMOTED`).
257    ///
258    /// Returns true if the object has been promoted to the old generation
259    /// in Ruby's generational GC.
260    ///
261    /// # Safety
262    /// - `obj` must be a valid VALUE
263    unsafe fn rb_obj_promoted(&self, obj: VALUE) -> bool;
264
265    /// Raw version assuming FL_ABLE (akin to `RB_OBJ_PROMOTED_RAW`).
266    ///
267    /// # Safety
268    /// - `obj` must be a valid heap-allocated Ruby object (FL_ABLE must be true)
269    unsafe fn rb_obj_promoted_raw(&self, obj: VALUE) -> bool;
270
271    /// Convert Ruby numeric to C double (akin to `NUM2DBL`).
272    ///
273    /// Works for Float (including Flonum), Fixnum, and other numeric types.
274    ///
275    /// # Safety
276    /// This function is unsafe because it may dereference a raw pointer to get
277    /// access to underlying Ruby data. The caller must ensure that the pointer
278    /// is valid.
279    unsafe fn num2dbl(&self, obj: VALUE) -> std::os::raw::c_double;
280
281    /// Convert C double to Ruby Float VALUE (akin to `DBL2NUM`).
282    ///
283    /// May return a Flonum (tagged pointer) for small values on 64-bit platforms,
284    /// or a heap-allocated Float object.
285    fn dbl2num(&self, val: std::os::raw::c_double) -> VALUE;
286
287    /// Get hash size (akin to `RHASH_SIZE`).
288    ///
289    /// Returns the number of entries in the hash.
290    ///
291    /// # Safety
292    /// This function is unsafe because it dereferences a raw pointer to get
293    /// access to underlying Ruby data. The caller must ensure that the pointer
294    /// is valid and points to a Hash object.
295    unsafe fn rhash_size(&self, obj: VALUE) -> usize;
296
297    /// Check if hash is empty (akin to `RHASH_EMPTY_P`).
298    ///
299    /// # Safety
300    /// This function is unsafe because it dereferences a raw pointer to get
301    /// access to underlying Ruby data. The caller must ensure that the pointer
302    /// is valid and points to a Hash object.
303    unsafe fn rhash_empty_p(&self, obj: VALUE) -> bool;
304
305    /// Get encoding index from object (akin to `ENCODING_GET`).
306    ///
307    /// Returns the encoding index stored in the object's flags.
308    ///
309    /// # Safety
310    /// This function is unsafe because it dereferences a raw pointer to get
311    /// access to underlying Ruby data. The caller must ensure that the pointer
312    /// is valid and points to an object with encoding (String, Regexp, Symbol).
313    unsafe fn encoding_get(&self, obj: VALUE) -> std::os::raw::c_int;
314
315    /// Check if an object can have flags (akin to `RB_FL_ABLE`).
316    ///
317    /// Returns false for immediate values (nil, true, false, Fixnum, Symbol, Flonum)
318    /// which don't have flag storage. Returns true for heap-allocated objects.
319    fn fl_able(&self, obj: VALUE) -> bool;
320
321    /// Convert Fixnum to long (akin to `FIX2LONG`).
322    ///
323    /// Extracts the integer value from a Fixnum VALUE.
324    ///
325    /// # Safety assumptions
326    /// - `obj` must be a valid Fixnum VALUE
327    fn fix2long(&self, obj: VALUE) -> std::os::raw::c_long;
328
329    /// Convert Fixnum to unsigned long (akin to `FIX2ULONG`).
330    ///
331    /// Extracts the unsigned integer value from a Fixnum VALUE.
332    ///
333    /// # Safety assumptions
334    /// - `obj` must be a valid positive Fixnum VALUE
335    fn fix2ulong(&self, obj: VALUE) -> std::os::raw::c_ulong;
336
337    /// Convert long to Fixnum (akin to `LONG2FIX`).
338    ///
339    /// Creates a Fixnum VALUE from a long integer.
340    ///
341    /// # Safety assumptions
342    /// - `val` must be in the valid Fixnum range
343    fn long2fix(&self, val: std::os::raw::c_long) -> VALUE;
344
345    /// Check if long value can be represented as Fixnum (akin to `FIXABLE`).
346    ///
347    /// Returns true if the value fits in a Fixnum.
348    fn fixable(&self, val: std::os::raw::c_long) -> bool;
349
350    /// Check if unsigned long value can be represented as positive Fixnum (akin to `POSFIXABLE`).
351    ///
352    /// Returns true if the value fits in a positive Fixnum.
353    fn posfixable(&self, val: std::os::raw::c_ulong) -> bool;
354
355    /// Convert Ruby Integer to long (akin to `NUM2LONG`).
356    ///
357    /// Converts any Ruby Integer (Fixnum or Bignum) to a C long.
358    /// May raise an exception if the value is out of range.
359    ///
360    /// # Safety
361    /// - `obj` must be a valid Integer VALUE
362    /// - May call into Ruby runtime (for Bignum conversion)
363    unsafe fn num2long(&self, obj: VALUE) -> std::os::raw::c_long;
364
365    /// Convert Ruby Integer to unsigned long (akin to `NUM2ULONG`).
366    ///
367    /// Converts any Ruby Integer (Fixnum or Bignum) to a C unsigned long.
368    /// May raise an exception if the value is out of range or negative.
369    ///
370    /// # Safety
371    /// - `obj` must be a valid Integer VALUE
372    /// - May call into Ruby runtime (for Bignum conversion)
373    unsafe fn num2ulong(&self, obj: VALUE) -> std::os::raw::c_ulong;
374
375    /// Convert long to Ruby Integer (akin to `LONG2NUM`).
376    ///
377    /// Creates a Ruby Integer (Fixnum or Bignum) from a C long.
378    /// Uses Fixnum if possible, otherwise allocates a Bignum.
379    fn long2num(&self, val: std::os::raw::c_long) -> VALUE;
380
381    /// Convert unsigned long to Ruby Integer (akin to `ULONG2NUM`).
382    ///
383    /// Creates a Ruby Integer (Fixnum or Bignum) from a C unsigned long.
384    /// Uses Fixnum if possible, otherwise allocates a Bignum.
385    fn ulong2num(&self, val: std::os::raw::c_ulong) -> VALUE;
386
387    /// Convert ID to Symbol (akin to `RB_ID2SYM`).
388    ///
389    /// Converts an internal ID to its corresponding Symbol VALUE.
390    /// This is a safe operation - just bit manipulation for static symbols.
391    fn id2sym(&self, id: ID) -> VALUE;
392
393    /// Convert Symbol to ID (akin to `RB_SYM2ID`).
394    ///
395    /// Converts a Symbol VALUE to its internal ID representation.
396    ///
397    /// # Safety
398    /// - `obj` must be a valid Symbol VALUE
399    /// - For dynamic symbols, this may access the heap
400    unsafe fn sym2id(&self, obj: VALUE) -> ID;
401
402    /// Execute GC write barrier when storing a reference (akin to `RB_OBJ_WRITE`).
403    ///
404    /// Must be called when storing a VALUE reference from one heap object to another.
405    /// This is critical for GC correctness - without it, the GC may collect objects
406    /// that are still referenced.
407    ///
408    /// # Safety
409    /// - `old` must be a valid heap-allocated Ruby object
410    /// - `slot` must be a valid pointer to a VALUE within `old`
411    /// - `young` must be a valid VALUE
412    unsafe fn rb_obj_write(&self, old: VALUE, slot: *mut VALUE, young: VALUE) -> VALUE;
413
414    /// Declare a write barrier without actually writing (akin to `RB_OBJ_WRITTEN`).
415    ///
416    /// Use this when you've already written a reference but need to inform the GC.
417    ///
418    /// # Safety
419    /// - `old` must be a valid heap-allocated Ruby object
420    /// - `oldv` is the previous value (can be any VALUE)
421    /// - `young` must be a valid VALUE that was written
422    unsafe fn rb_obj_written(&self, old: VALUE, oldv: VALUE, young: VALUE) -> VALUE;
423}
424
425#[cfg(stable_api_enable_compiled_mod)]
426mod compiled;
427#[cfg(stable_api_export_compiled_as_api)]
428use compiled as api;
429
430#[cfg(stable_api_include_rust_impl)]
431#[cfg_attr(ruby_eq_2_7, path = "stable_api/ruby_2_7.rs")]
432#[cfg_attr(ruby_eq_3_0, path = "stable_api/ruby_3_0.rs")]
433#[cfg_attr(ruby_eq_3_1, path = "stable_api/ruby_3_1.rs")]
434#[cfg_attr(ruby_eq_3_2, path = "stable_api/ruby_3_2.rs")]
435#[cfg_attr(ruby_eq_3_3, path = "stable_api/ruby_3_3.rs")]
436#[cfg_attr(ruby_eq_3_4, path = "stable_api/ruby_3_4.rs")]
437#[cfg_attr(ruby_eq_4_0, path = "stable_api/ruby_4_0.rs")]
438mod rust;
439#[cfg(not(stable_api_export_compiled_as_api))]
440use rust as api;
441
442impl std::fmt::Debug for api::Definition {
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        f.debug_struct("StableApiDefinition")
445            .field("VERSION_MAJOR", &api::Definition::VERSION_MAJOR)
446            .field("VERSION_MINOR", &api::Definition::VERSION_MINOR)
447            .finish()
448    }
449}
450
451/// Get the default stable API definition for the current Ruby version.
452#[inline(always)]
453pub const fn get_default() -> &'static api::Definition {
454    const API: api::Definition = api::Definition {};
455    &API
456}
457
458/// Get the fallback stable API definition for the current Ruby version, which
459/// is compiled C code that is linked into to this crate.
460#[inline(always)]
461#[cfg(stable_api_enable_compiled_mod)]
462pub const fn get_compiled() -> &'static compiled::Definition {
463    const COMPILED_API: compiled::Definition = compiled::Definition {};
464    &COMPILED_API
465}