artichoke_backend/extn/core/array/
ffi.rs

1use std::ptr::NonNull;
2use std::slice;
3
4use crate::extn::core::array::{Array, RawParts};
5use crate::extn::prelude::*;
6
7// ```c
8// MRB_API mrb_value mrb_ary_new(mrb_state *mrb);
9// ```
10#[unsafe(no_mangle)]
11unsafe extern "C-unwind" fn mrb_ary_new(mrb: *mut sys::mrb_state) -> sys::mrb_value {
12    unwrap_interpreter!(mrb, to => guard);
13    let result = Array::new();
14    let result = Array::alloc_value(result, &mut guard);
15    match result {
16        Ok(value) => value.inner(),
17        Err(exception) => {
18            // SAFETY: only Copy objects remain on the stack
19            unsafe { error::raise(guard, exception) }
20        }
21    }
22}
23
24// ```c
25// MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int);
26// ```
27#[unsafe(no_mangle)]
28unsafe extern "C-unwind" fn mrb_ary_new_capa(mrb: *mut sys::mrb_state, capa: sys::mrb_int) -> sys::mrb_value {
29    unwrap_interpreter!(mrb, to => guard);
30    let capacity = usize::try_from(capa).unwrap_or_default();
31    let result = Array::with_capacity(capacity);
32    let result = Array::alloc_value(result, &mut guard);
33    match result {
34        Ok(value) => value.inner(),
35        Err(exception) => {
36            // SAFETY: only Copy objects remain on the stack
37            unsafe { error::raise(guard, exception) }
38        }
39    }
40}
41
42// ```c
43// MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals);
44// ```
45#[unsafe(no_mangle)]
46unsafe extern "C-unwind" fn mrb_ary_new_from_values(
47    mrb: *mut sys::mrb_state,
48    size: sys::mrb_int,
49    vals: *const sys::mrb_value,
50) -> sys::mrb_value {
51    unwrap_interpreter!(mrb, to => guard);
52    let size = usize::try_from(size).unwrap_or_default();
53    let values = slice::from_raw_parts(vals, size);
54    let result = Array::from(values);
55    let result = Array::alloc_value(result, &mut guard);
56    match result {
57        Ok(value) => {
58            let basic = sys::mrb_sys_basic_ptr(value.inner());
59            sys::mrb_write_barrier(mrb, basic);
60            value.inner()
61        }
62        Err(exception) => {
63            // SAFETY: only Copy objects remain on the stack
64            unsafe { error::raise(guard, exception) }
65        }
66    }
67}
68
69// ```c
70// MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr)
71// ```
72#[unsafe(no_mangle)]
73unsafe extern "C-unwind" fn mrb_assoc_new(
74    mrb: *mut sys::mrb_state,
75    one: sys::mrb_value,
76    two: sys::mrb_value,
77) -> sys::mrb_value {
78    unwrap_interpreter!(mrb, to => guard);
79    let result = Array::assoc(one.into(), two.into());
80    let result = Array::alloc_value(result, &mut guard);
81    match result {
82        Ok(value) => {
83            let basic = sys::mrb_sys_basic_ptr(value.inner());
84            sys::mrb_write_barrier(mrb, basic);
85            value.inner()
86        }
87        Err(exception) => {
88            // SAFETY: only Copy objects remain on the stack
89            unsafe { error::raise(guard, exception) }
90        }
91    }
92}
93
94// ```c
95// MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value value);
96// ```
97#[unsafe(no_mangle)]
98unsafe extern "C-unwind" fn mrb_ary_splat(mrb: *mut sys::mrb_state, value: sys::mrb_value) -> sys::mrb_value {
99    unwrap_interpreter!(mrb, to => guard);
100    let mut value = Value::from(value);
101    let result = if Array::unbox_from_value(&mut value, &mut guard).is_ok() {
102        Ok(value)
103    } else {
104        let mut result = Array::with_capacity(1);
105        result.push(value);
106        Array::alloc_value(result, &mut guard)
107    };
108    match result {
109        Ok(value) => {
110            let basic = sys::mrb_sys_basic_ptr(value.inner());
111            sys::mrb_write_barrier(mrb, basic);
112            value.inner()
113        }
114        Err(exception) => {
115            // SAFETY: only Copy objects remain on the stack
116            unsafe { error::raise(guard, exception) }
117        }
118    }
119}
120
121// ```c
122// MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other);
123// ```
124//
125// This function corresponds to the `OP_ARYCAT` VM opcode.
126#[unsafe(no_mangle)]
127unsafe extern "C-unwind" fn mrb_ary_concat(mrb: *mut sys::mrb_state, ary: sys::mrb_value, other: sys::mrb_value) {
128    unwrap_interpreter!(mrb, to => guard, or_else = ());
129    let mut array = Value::from(ary);
130    let mut other = Value::from(other);
131    if let Ok(mut array) = Array::unbox_from_value(&mut array, &mut guard) {
132        if let Ok(other) = Array::unbox_from_value(&mut other, &mut guard) {
133            // SAFETY: The array is repacked before any intervening uses of
134            // `interp` which means no mruby heap allocations can occur.
135            let array_mut = array.as_inner_mut();
136            array_mut.extend(other.iter());
137
138            let inner = array.take();
139            Array::box_into_value(inner, ary.into(), &mut guard).expect("Array reboxing should not fail");
140        } else {
141            emit_fatal_warning!(
142                "ffi: mrb_ary_concat: Expected {:?} argument but got {:?} argument",
143                Ruby::Array,
144                other.ruby_type()
145            );
146        }
147
148        let basic = sys::mrb_sys_basic_ptr(ary);
149        sys::mrb_write_barrier(mrb, basic);
150    }
151}
152
153// ```c
154// MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary);
155// ```
156#[unsafe(no_mangle)]
157unsafe extern "C-unwind" fn mrb_ary_pop(mrb: *mut sys::mrb_state, ary: sys::mrb_value) -> sys::mrb_value {
158    unwrap_interpreter!(mrb, to => guard);
159    let mut array = Value::from(ary);
160    let result = if let Ok(mut array) = Array::unbox_from_value(&mut array, &mut guard) {
161        // SAFETY: The array is repacked before any intervening uses of `interp`
162        // which means no mruby heap allocations can occur.
163        let array_mut = array.as_inner_mut();
164        let popped = array_mut.pop();
165
166        let inner = array.take();
167        Array::box_into_value(inner, ary.into(), &mut guard).expect("Array reboxing should not fail");
168
169        guard.convert(popped)
170    } else {
171        Value::nil()
172    };
173    let basic = sys::mrb_sys_basic_ptr(ary);
174    sys::mrb_write_barrier(mrb, basic);
175    result.inner()
176}
177
178// ```c
179// MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value);
180// ```
181#[unsafe(no_mangle)]
182unsafe extern "C-unwind" fn mrb_ary_push(mrb: *mut sys::mrb_state, ary: sys::mrb_value, value: sys::mrb_value) {
183    unwrap_interpreter!(mrb, to => guard, or_else = ());
184    let mut array = Value::from(ary);
185    let value = Value::from(value);
186    if let Ok(mut array) = Array::unbox_from_value(&mut array, &mut guard) {
187        // SAFETY: The array is repacked before any intervening uses of `interp`
188        // which means no mruby heap allocations can occur.
189        let array_mut = array.as_inner_mut();
190        array_mut.push(value);
191
192        let inner = array.take();
193        Array::box_into_value(inner, ary.into(), &mut guard).expect("Array reboxing should not fail");
194    }
195    let basic = sys::mrb_sys_basic_ptr(ary);
196    sys::mrb_write_barrier(mrb, basic);
197}
198
199// ```c
200// MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
201// ```
202#[unsafe(no_mangle)]
203unsafe extern "C-unwind" fn mrb_ary_ref(
204    mrb: *mut sys::mrb_state,
205    ary: sys::mrb_value,
206    offset: sys::mrb_int,
207) -> sys::mrb_value {
208    unwrap_interpreter!(mrb, to => guard);
209    let mut ary = Value::from(ary);
210    let offset = usize::try_from(offset).unwrap_or_default();
211    let result = if let Ok(array) = Array::unbox_from_value(&mut ary, &mut guard) {
212        guard.convert(array.get(offset))
213    } else {
214        Value::nil()
215    };
216    result.inner()
217}
218
219// ```c
220// MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
221// ```
222#[unsafe(no_mangle)]
223unsafe extern "C-unwind" fn mrb_ary_set(
224    mrb: *mut sys::mrb_state,
225    ary: sys::mrb_value,
226    offset: sys::mrb_int,
227    value: sys::mrb_value,
228) {
229    unwrap_interpreter!(mrb, to => guard, or_else = ());
230    let mut array = Value::from(ary);
231    let value = Value::from(value);
232    if let Ok(mut array) = Array::unbox_from_value(&mut array, &mut guard) {
233        let offset = aref::offset_to_index(offset, array.len()).unwrap_or(0);
234        // TODO: properly handle self-referential sets.
235        if Value::from(ary) != value {
236            // SAFETY: The array is repacked before any intervening uses of
237            // `interp` which means no mruby heap allocations can occur.
238            let array_mut = array.as_inner_mut();
239            array_mut.set(offset, value);
240
241            let inner = array.take();
242            Array::box_into_value(inner, ary.into(), &mut guard).expect("Array reboxing should not fail");
243        }
244    }
245    let basic = sys::mrb_sys_basic_ptr(ary);
246    sys::mrb_write_barrier(mrb, basic);
247}
248
249// ```c
250// MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self)
251// ```
252#[unsafe(no_mangle)]
253unsafe extern "C-unwind" fn mrb_ary_shift(mrb: *mut sys::mrb_state, ary: sys::mrb_value) -> sys::mrb_value {
254    unwrap_interpreter!(mrb, to => guard);
255    let mut array = Value::from(ary);
256    let result = if let Ok(mut array) = Array::unbox_from_value(&mut array, &mut guard) {
257        // SAFETY: The array is repacked before any intervening uses of `interp`
258        // which means no mruby heap allocations can occur.
259        let array_mut = array.as_inner_mut();
260        let result = array_mut.shift();
261
262        let inner = array.take();
263        Array::box_into_value(inner, ary.into(), &mut guard).expect("Array reboxing should not fail");
264
265        guard.convert(result)
266    } else {
267        Value::nil()
268    };
269    let basic = sys::mrb_sys_basic_ptr(ary);
270    sys::mrb_write_barrier(mrb, basic);
271    result.inner()
272}
273
274// ```c
275// MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item)
276// ```
277#[unsafe(no_mangle)]
278unsafe extern "C-unwind" fn mrb_ary_unshift(
279    mrb: *mut sys::mrb_state,
280    ary: sys::mrb_value,
281    value: sys::mrb_value,
282) -> sys::mrb_value {
283    unwrap_interpreter!(mrb, to => guard);
284    let mut array = Value::from(ary);
285    if let Ok(mut array) = Array::unbox_from_value(&mut array, &mut guard) {
286        // SAFETY: The array is repacked before any intervening uses of `interp`
287        // which means no mruby heap allocations can occur.
288        let array_mut = array.as_inner_mut();
289        array_mut.unshift(value.into());
290
291        let inner = array.take();
292        Array::box_into_value(inner, ary.into(), &mut guard).expect("Array reboxing should not fail");
293    }
294    let basic = sys::mrb_sys_basic_ptr(ary);
295    sys::mrb_write_barrier(mrb, basic);
296    value
297}
298
299#[unsafe(no_mangle)]
300#[expect(
301    clippy::cast_possible_truncation,
302    clippy::cast_sign_loss,
303    reason = "mruby stores sizes as int64_t instead of size_t"
304)]
305unsafe extern "C-unwind" fn mrb_ary_artichoke_free(mrb: *mut sys::mrb_state, ary: *mut sys::RArray) {
306    let _ = mrb;
307
308    let Some(mut ptr) = NonNull::new((*ary).as_.heap.ptr) else {
309        // An allocated but uninitialized array has a null pointer, so there is
310        // nothing to free.
311        return;
312    };
313    let length = (*ary).as_.heap.len as usize;
314    let capacity = (*ary).as_.heap.aux.capa as usize;
315
316    drop(Array::from_raw_parts(RawParts {
317        ptr: ptr.as_mut(),
318        length,
319        capacity,
320    }));
321}