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 /// Gets the data type from an RTypedData object.
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_type(&self, obj: VALUE) -> *const crate::rb_data_type_t;
205
206 /// Gets the data pointer 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_get_data(&self, obj: VALUE) -> *mut std::ffi::c_void;
213
214 /// Convert Fixnum to long (akin to `FIX2LONG`).
215 ///
216 /// Extracts the integer value from a Fixnum VALUE.
217 ///
218 /// # Safety assumptions
219 /// - `obj` must be a valid Fixnum VALUE
220 fn fix2long(&self, obj: VALUE) -> std::os::raw::c_long;
221
222 /// Convert Fixnum to unsigned long (akin to `FIX2ULONG`).
223 ///
224 /// Extracts the unsigned integer value from a Fixnum VALUE.
225 ///
226 /// # Safety assumptions
227 /// - `obj` must be a valid positive Fixnum VALUE
228 fn fix2ulong(&self, obj: VALUE) -> std::os::raw::c_ulong;
229
230 /// Convert long to Fixnum (akin to `LONG2FIX`).
231 ///
232 /// Creates a Fixnum VALUE from a long integer.
233 ///
234 /// # Safety assumptions
235 /// - `val` must be in the valid Fixnum range
236 fn long2fix(&self, val: std::os::raw::c_long) -> VALUE;
237
238 /// Check if long value can be represented as Fixnum (akin to `FIXABLE`).
239 ///
240 /// Returns true if the value fits in a Fixnum.
241 fn fixable(&self, val: std::os::raw::c_long) -> bool;
242
243 /// Check if unsigned long value can be represented as positive Fixnum (akin to `POSFIXABLE`).
244 ///
245 /// Returns true if the value fits in a positive Fixnum.
246 fn posfixable(&self, val: std::os::raw::c_ulong) -> bool;
247
248 /// Convert Ruby Integer to long (akin to `NUM2LONG`).
249 ///
250 /// Converts any Ruby Integer (Fixnum or Bignum) to a C long.
251 /// May raise an exception if the value is out of range.
252 ///
253 /// # Safety
254 /// - `obj` must be a valid Integer VALUE
255 /// - May call into Ruby runtime (for Bignum conversion)
256 unsafe fn num2long(&self, obj: VALUE) -> std::os::raw::c_long;
257
258 /// Convert Ruby Integer to unsigned long (akin to `NUM2ULONG`).
259 ///
260 /// Converts any Ruby Integer (Fixnum or Bignum) to a C unsigned long.
261 /// May raise an exception if the value is out of range or negative.
262 ///
263 /// # Safety
264 /// - `obj` must be a valid Integer VALUE
265 /// - May call into Ruby runtime (for Bignum conversion)
266 unsafe fn num2ulong(&self, obj: VALUE) -> std::os::raw::c_ulong;
267
268 /// Convert long to Ruby Integer (akin to `LONG2NUM`).
269 ///
270 /// Creates a Ruby Integer (Fixnum or Bignum) from a C long.
271 /// Uses Fixnum if possible, otherwise allocates a Bignum.
272 fn long2num(&self, val: std::os::raw::c_long) -> VALUE;
273
274 /// Convert unsigned long to Ruby Integer (akin to `ULONG2NUM`).
275 ///
276 /// Creates a Ruby Integer (Fixnum or Bignum) from a C unsigned long.
277 /// Uses Fixnum if possible, otherwise allocates a Bignum.
278 fn ulong2num(&self, val: std::os::raw::c_ulong) -> VALUE;
279
280 /// Convert ID to Symbol (akin to `RB_ID2SYM`).
281 ///
282 /// Converts an internal ID to its corresponding Symbol VALUE.
283 /// This is a safe operation - just bit manipulation for static symbols.
284 fn id2sym(&self, id: ID) -> VALUE;
285
286 /// Convert Symbol to ID (akin to `RB_SYM2ID`).
287 ///
288 /// Converts a Symbol VALUE to its internal ID representation.
289 ///
290 /// # Safety
291 /// - `obj` must be a valid Symbol VALUE
292 /// - For dynamic symbols, this may access the heap
293 unsafe fn sym2id(&self, obj: VALUE) -> ID;
294}
295
296#[cfg(stable_api_enable_compiled_mod)]
297mod compiled;
298#[cfg(stable_api_export_compiled_as_api)]
299use compiled as api;
300
301#[cfg(stable_api_include_rust_impl)]
302#[cfg_attr(ruby_eq_2_7, path = "stable_api/ruby_2_7.rs")]
303#[cfg_attr(ruby_eq_3_0, path = "stable_api/ruby_3_0.rs")]
304#[cfg_attr(ruby_eq_3_1, path = "stable_api/ruby_3_1.rs")]
305#[cfg_attr(ruby_eq_3_2, path = "stable_api/ruby_3_2.rs")]
306#[cfg_attr(ruby_eq_3_3, path = "stable_api/ruby_3_3.rs")]
307#[cfg_attr(ruby_eq_3_4, path = "stable_api/ruby_3_4.rs")]
308#[cfg_attr(ruby_eq_4_0, path = "stable_api/ruby_4_0.rs")]
309mod rust;
310#[cfg(not(stable_api_export_compiled_as_api))]
311use rust as api;
312
313impl std::fmt::Debug for api::Definition {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 f.debug_struct("StableApiDefinition")
316 .field("VERSION_MAJOR", &api::Definition::VERSION_MAJOR)
317 .field("VERSION_MINOR", &api::Definition::VERSION_MINOR)
318 .finish()
319 }
320}
321
322/// Get the default stable API definition for the current Ruby version.
323#[inline(always)]
324pub const fn get_default() -> &'static api::Definition {
325 const API: api::Definition = api::Definition {};
326 &API
327}
328
329/// Get the fallback stable API definition for the current Ruby version, which
330/// is compiled C code that is linked into to this crate.
331#[inline(always)]
332#[cfg(stable_api_enable_compiled_mod)]
333pub const fn get_compiled() -> &'static compiled::Definition {
334 const COMPILED_API: compiled::Definition = compiled::Definition {};
335 &COMPILED_API
336}