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}