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}