artichoke_backend/
value.rs

1//! Boxed values on the Ruby interpreter heap.
2
3use std::borrow::Cow;
4use std::error;
5use std::fmt;
6use std::ptr;
7
8use artichoke_core::convert::{Convert, ConvertMut, TryConvertMut};
9use artichoke_core::intern::Intern;
10use artichoke_core::value::Value as ValueCore;
11use spinoso_exception::{ArgumentError, Fatal};
12
13use crate::Artichoke;
14use crate::core::ClassRegistry;
15use crate::error::{Error, RubyException};
16use crate::exception_handler;
17use crate::gc::MrbGarbageCollection;
18use crate::sys::{self, protect};
19use crate::types::{self, Ruby};
20
21/// Max argument count for function calls including initialize and yield.
22pub const MRB_FUNCALL_ARGC_MAX: usize = 16;
23
24/// Boxed Ruby value in the [`Artichoke`] interpreter.
25#[derive(Default, Debug, Clone, Copy)]
26pub struct Value(sys::mrb_value);
27
28impl From<Value> for sys::mrb_value {
29    /// Extract the inner [`sys::mrb_value`] from this [`Value`].
30    fn from(value: Value) -> Self {
31        value.0
32    }
33}
34
35impl From<sys::mrb_value> for Value {
36    /// Construct a new [`Value`] from a [`sys::mrb_value`].
37    fn from(value: sys::mrb_value) -> Self {
38        Self(value)
39    }
40}
41
42impl From<Option<sys::mrb_value>> for Value {
43    fn from(value: Option<sys::mrb_value>) -> Self {
44        let Some(value) = value else {
45            return Self::nil();
46        };
47        Self::from(value)
48    }
49}
50
51impl From<Option<Value>> for Value {
52    fn from(value: Option<Value>) -> Self {
53        value.unwrap_or_else(Value::nil)
54    }
55}
56
57impl PartialEq for Value {
58    fn eq(&self, other: &Self) -> bool {
59        let this = unsafe { sys::mrb_sys_basic_ptr(self.inner()) };
60        let other = unsafe { sys::mrb_sys_basic_ptr(other.inner()) };
61        ptr::eq(this, other)
62    }
63}
64
65impl Value {
66    /// Create a new, empty Ruby value.
67    ///
68    /// Alias for `Value::default`.
69    #[inline]
70    #[must_use]
71    pub fn new() -> Self {
72        Self::default()
73    }
74
75    /// Create a `nil` Ruby Value.
76    #[inline]
77    #[must_use]
78    pub fn nil() -> Self {
79        Self::default()
80    }
81
82    /// Retrieve the inner [`sys::mrb_value`] that this [`Value`] wraps.
83    #[inline]
84    #[must_use]
85    pub(crate) const fn inner(&self) -> sys::mrb_value {
86        self.0
87    }
88
89    /// Whether a value is an interpreter-only variant not exposed to Ruby.
90    ///
91    /// Some type tags like [`MRB_TT_UNDEF`](sys::mrb_vtype::MRB_TT_UNDEF) are
92    /// internal to the mruby VM and manipulating them with the [`sys`] API is
93    /// unspecified and may result in a segfault.
94    ///
95    /// After extracting a [`sys::mrb_value`] from the interpreter, check to see
96    /// if the value is [unreachable](Ruby::Unreachable) a [`Fatal`] exception.
97    ///
98    /// See: [mruby#4460](https://github.com/mruby/mruby/issues/4460).
99    #[must_use]
100    #[inline]
101    pub fn is_unreachable(&self) -> bool {
102        matches!(self.ruby_type(), Ruby::Unreachable)
103    }
104
105    /// Return whether this object is unreachable by any GC roots.
106    #[must_use]
107    pub fn is_dead(&self, interp: &mut Artichoke) -> bool {
108        let value = self.inner();
109        let is_dead = unsafe { interp.with_ffi_boundary(|mrb| sys::mrb_sys_value_is_dead(mrb, value)) };
110        is_dead.unwrap_or_default()
111    }
112
113    pub fn is_range(&self, interp: &mut Artichoke, len: i64) -> Result<Option<protect::Range>, Error> {
114        let mut arena = interp.create_arena_savepoint()?;
115        let result = unsafe {
116            arena
117                .interp()
118                .with_ffi_boundary(|mrb| protect::is_range(mrb, self.inner(), len))?
119        };
120        match result {
121            Ok(range) => Ok(range),
122            Err(exception) => {
123                let exception = Self::from(exception);
124                Err(exception_handler::last_error(&mut arena, exception)?)
125            }
126        }
127    }
128}
129
130impl ValueCore for Value {
131    type Artichoke = Artichoke;
132    type Arg = Self;
133    type Value = Self;
134    type Block = Self;
135    type Error = Error;
136
137    fn funcall(
138        &self,
139        interp: &mut Self::Artichoke,
140        func: &str,
141        args: &[Self::Arg],
142        block: Option<Self::Block>,
143    ) -> Result<Self::Value, Self::Error> {
144        if self.is_dead(interp) {
145            let message = "Value receiver for function call is dead. \
146                           This indicates a bug in the mruby garbage collector. \
147                           Please leave a comment at https://github.com/artichoke/artichoke/issues/1336.";
148            return Err(Fatal::with_message(message).into());
149        }
150        if let Ok(arg_count_error) = ArgCountError::try_from(args) {
151            emit_fatal_warning!("Value::funcall invoked with too many arguments: {}", arg_count_error);
152            return Err(arg_count_error.into());
153        }
154        let args = args.iter().map(Self::inner).collect::<Vec<_>>();
155        let func = interp.intern_string(func.to_string())?;
156        let result = unsafe {
157            interp.with_ffi_boundary(|mrb| {
158                protect::funcall(
159                    mrb,
160                    self.inner(),
161                    func,
162                    args.as_slice(),
163                    block.as_ref().map(Self::inner),
164                )
165            })?
166        };
167        match result {
168            Ok(value) => {
169                let value = Self::from(value);
170                if value.is_unreachable() {
171                    // Unreachable values are internal to the mruby interpreter
172                    // and interacting with them via the C API is unspecified
173                    // and may result in a segfault.
174                    //
175                    // See: <https://github.com/mruby/mruby/issues/4460>
176                    Err(Fatal::from("Unreachable Ruby value").into())
177                } else {
178                    Ok(interp.protect(value))
179                }
180            }
181            Err(exception) => {
182                let exception = interp.protect(Self::from(exception));
183                Err(exception_handler::last_error(interp, exception)?)
184            }
185        }
186    }
187
188    fn freeze(&mut self, interp: &mut Self::Artichoke) -> Result<(), Self::Error> {
189        self.funcall(interp, "freeze", &[], None)?;
190        Ok(())
191    }
192
193    fn is_frozen(&self, interp: &mut Self::Artichoke) -> bool {
194        let value = self.inner();
195        let is_frozen = unsafe { interp.with_ffi_boundary(|mrb| sys::mrb_sys_obj_frozen(mrb, value)) };
196        is_frozen.unwrap_or_default()
197    }
198
199    fn inspect(&self, interp: &mut Self::Artichoke) -> Vec<u8> {
200        let Ok(display) = self.funcall(interp, "inspect", &[], None) else {
201            return vec![];
202        };
203        display.try_convert_into_mut(interp).unwrap_or_default()
204    }
205
206    fn is_nil(&self) -> bool {
207        matches!(self.ruby_type(), Ruby::Nil)
208    }
209
210    fn respond_to(&self, interp: &mut Self::Artichoke, method: &str) -> Result<bool, Self::Error> {
211        // Look up a method in the mruby VM's method table for this value's
212        // class object.
213        let method_sym = if let Some(sym) = interp.check_interned_string(method)? {
214            sym
215        } else {
216            interp.intern_string(String::from(method))?
217        };
218        let has_method =
219            unsafe { interp.with_ffi_boundary(|mrb| sys::mrb_sys_value_has_method(mrb, self.inner(), method_sym))? };
220        Ok(has_method)
221    }
222
223    fn to_s(&self, interp: &mut Self::Artichoke) -> Vec<u8> {
224        let Ok(display) = self.funcall(interp, "to_s", &[], None) else {
225            return vec![];
226        };
227        display.try_convert_into_mut(interp).unwrap_or_default()
228    }
229
230    fn ruby_type(&self) -> Ruby {
231        types::ruby_from_mrb_value(self.inner())
232    }
233}
234
235impl Convert<Value, Value> for Artichoke {
236    fn convert(&self, value: Value) -> Value {
237        value
238    }
239}
240
241impl ConvertMut<Value, Value> for Artichoke {
242    fn convert_mut(&mut self, value: Value) -> Value {
243        value
244    }
245}
246
247/// Argument count exceeds maximum allowed by the VM.
248#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
249pub struct ArgCountError {
250    /// Number of arguments given.
251    pub given: usize,
252    /// Maximum number of arguments supported.
253    pub max: usize,
254}
255
256impl TryFrom<Vec<Value>> for ArgCountError {
257    type Error = ();
258
259    fn try_from(args: Vec<Value>) -> Result<Self, Self::Error> {
260        args.as_slice().try_into()
261    }
262}
263
264impl TryFrom<Vec<sys::mrb_value>> for ArgCountError {
265    type Error = ();
266
267    fn try_from(args: Vec<sys::mrb_value>) -> Result<Self, Self::Error> {
268        args.as_slice().try_into()
269    }
270}
271
272impl TryFrom<&[Value]> for ArgCountError {
273    type Error = ();
274
275    fn try_from(args: &[Value]) -> Result<Self, Self::Error> {
276        // Arg count is ok, so we failed ton construct the error.
277        if args.len() <= MRB_FUNCALL_ARGC_MAX {
278            return Err(());
279        }
280        let err = Self {
281            given: args.len(),
282            max: MRB_FUNCALL_ARGC_MAX,
283        };
284        Ok(err)
285    }
286}
287
288impl TryFrom<&[sys::mrb_value]> for ArgCountError {
289    type Error = ();
290
291    fn try_from(args: &[sys::mrb_value]) -> Result<Self, Self::Error> {
292        // Arg count is ok, so we failed ton construct the error.
293        if args.len() <= MRB_FUNCALL_ARGC_MAX {
294            return Err(());
295        }
296        let err = Self {
297            given: args.len(),
298            max: MRB_FUNCALL_ARGC_MAX,
299        };
300        Ok(err)
301    }
302}
303
304impl ArgCountError {
305    /// Constructs a new, empty `ArgCountError`.
306    #[must_use]
307    pub fn new() -> Self {
308        Self {
309            given: 0,
310            max: MRB_FUNCALL_ARGC_MAX,
311        }
312    }
313}
314
315impl fmt::Display for ArgCountError {
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317        f.write_str("Too many arguments for function call: ")?;
318        write!(
319            f,
320            "gave {} arguments, but Artichoke only supports a maximum of {} arguments",
321            self.given, self.max
322        )
323    }
324}
325
326impl error::Error for ArgCountError {}
327
328impl RubyException for ArgCountError {
329    fn message(&self) -> Cow<'_, [u8]> {
330        Cow::Borrowed(b"Too many arguments")
331    }
332
333    fn name(&self) -> Cow<'_, str> {
334        "ArgumentError".into()
335    }
336
337    fn vm_backtrace(&self, interp: &mut Artichoke) -> Option<Vec<Vec<u8>>> {
338        let _ = interp;
339        None
340    }
341
342    fn as_mrb_value(&self, interp: &mut Artichoke) -> Option<sys::mrb_value> {
343        let message = interp.try_convert_mut(self.to_string()).ok()?;
344        let value = interp.new_instance::<ArgumentError>(&[message]).ok().flatten()?;
345        Some(value.inner())
346    }
347}
348
349impl From<ArgCountError> for Error {
350    fn from(exception: ArgCountError) -> Self {
351        let err: Box<dyn RubyException> = Box::new(exception);
352        Self::from(err)
353    }
354}
355
356#[cfg(test)]
357mod tests {
358    use core::panic;
359
360    use bstr::ByteSlice;
361
362    use super::{ArgCountError, MRB_FUNCALL_ARGC_MAX};
363    use crate::gc::MrbGarbageCollection;
364    use crate::test::prelude::*;
365
366    #[test]
367    fn to_s_true() {
368        let mut interp = interpreter();
369
370        let value = interp.convert(true);
371        let string = value.to_s(&mut interp);
372        assert_eq!(string.as_bstr(), b"true".as_bstr());
373    }
374
375    #[test]
376    fn inspect_true() {
377        let mut interp = interpreter();
378
379        let value = interp.convert(true);
380        let debug = value.inspect(&mut interp);
381        assert_eq!(debug.as_bstr(), b"true".as_bstr());
382    }
383
384    #[test]
385    fn to_s_false() {
386        let mut interp = interpreter();
387
388        let value = interp.convert(false);
389        let string = value.to_s(&mut interp);
390        assert_eq!(string.as_bstr(), b"false".as_bstr());
391    }
392
393    #[test]
394    fn inspect_false() {
395        let mut interp = interpreter();
396
397        let value = interp.convert(false);
398        let debug = value.inspect(&mut interp);
399        assert_eq!(debug.as_bstr(), b"false".as_bstr());
400    }
401
402    #[test]
403    fn to_s_nil() {
404        let mut interp = interpreter();
405
406        let value = Value::nil();
407        let string = value.to_s(&mut interp);
408        assert_eq!(string.as_bstr(), b"".as_bstr());
409    }
410
411    #[test]
412    fn inspect_nil() {
413        let mut interp = interpreter();
414
415        let value = Value::nil();
416        let debug = value.inspect(&mut interp);
417        assert_eq!(debug.as_bstr(), b"nil".as_bstr());
418    }
419
420    #[test]
421    fn to_s_fixnum() {
422        let mut interp = interpreter();
423
424        let value = Convert::<_, Value>::convert(&*interp, 255);
425        let string = value.to_s(&mut interp);
426        assert_eq!(string.as_bstr(), b"255".as_bstr());
427    }
428
429    #[test]
430    fn inspect_fixnum() {
431        let mut interp = interpreter();
432
433        let value = Convert::<_, Value>::convert(&*interp, 255);
434        let debug = value.inspect(&mut interp);
435        assert_eq!(debug.as_bstr(), b"255".as_bstr());
436    }
437
438    #[test]
439    fn to_s_string() {
440        let mut interp = interpreter();
441
442        let value = interp.try_convert_mut("interstate").unwrap();
443        let string = value.to_s(&mut interp);
444        assert_eq!(string.as_bstr(), b"interstate".as_bstr());
445    }
446
447    #[test]
448    fn inspect_string() {
449        let mut interp = interpreter();
450
451        let value = interp.try_convert_mut("interstate").unwrap();
452        let debug = value.inspect(&mut interp);
453        assert_eq!(debug.as_bstr(), br#""interstate""#.as_bstr());
454    }
455
456    #[test]
457    fn to_s_empty_string() {
458        let mut interp = interpreter();
459
460        let value = interp.try_convert_mut("").unwrap();
461        let string = value.to_s(&mut interp);
462        assert_eq!(string.as_bstr(), b"".as_bstr());
463    }
464
465    #[test]
466    fn inspect_empty_string() {
467        let mut interp = interpreter();
468
469        let value = interp.try_convert_mut("").unwrap();
470        let debug = value.inspect(&mut interp);
471        assert_eq!(debug.as_bstr(), br#""""#.as_bstr());
472    }
473
474    #[test]
475    fn is_dead() {
476        let mut interp = interpreter();
477        let mut arena = interp.create_arena_savepoint().unwrap();
478        let live = arena.eval(b"'dead'").unwrap();
479        assert!(!live.is_dead(&mut arena));
480
481        let dead = live;
482        let live = arena.eval(b"'live'").unwrap();
483        arena.restore();
484        interp.full_gc().unwrap();
485
486        // unreachable objects are dead after a full garbage collection
487        assert!(dead.is_dead(&mut interp));
488        // the result of the most recent eval is always live even after a full
489        // garbage collection
490        assert!(!live.is_dead(&mut interp));
491    }
492
493    #[test]
494    fn funcall_is_dead() {
495        let mut interp = interpreter();
496        let mut arena = interp.create_arena_savepoint().unwrap();
497
498        let dead = arena.eval(b"'dead'").unwrap();
499        arena.eval(b"'live'").unwrap();
500        arena.restore();
501        interp.full_gc().unwrap();
502
503        assert!(dead.is_dead(&mut interp));
504
505        let error = dead.funcall(&mut interp, "nil?", &[], None).unwrap_err();
506        assert_eq!(error.name().as_ref(), "fatal");
507    }
508
509    #[test]
510    fn immediate_is_dead() {
511        let mut interp = interpreter();
512        let mut arena = interp.create_arena_savepoint().unwrap();
513        let live = arena.eval(b"27").unwrap();
514        assert!(!live.is_dead(&mut arena));
515
516        let immediate = live;
517        let live = arena.eval(b"64").unwrap();
518        arena.restore();
519        interp.full_gc().unwrap();
520
521        // immediate objects are never dead
522        assert!(!immediate.is_dead(&mut interp));
523        // the result of the most recent eval is always live even after a full
524        // garbage collection
525        assert!(!live.is_dead(&mut interp));
526
527        // `Fixnum`s are immediate even if they are created directly without an
528        // interpreter.
529        let fixnum = Convert::<_, Value>::convert(&*interp, 99);
530        assert!(!fixnum.is_dead(&mut interp));
531    }
532
533    #[test]
534    fn funcall_nil_nil() {
535        let mut interp = interpreter();
536
537        let nil = Value::nil();
538        let result = nil
539            .funcall(&mut interp, "nil?", &[], None)
540            .and_then(|value| value.try_convert_into::<bool>(&interp));
541        let nil_is_nil = unwrap_or_panic_with_backtrace(&mut interp, "Value::funcall", result);
542        assert!(nil_is_nil);
543    }
544
545    #[test]
546    fn funcall_string_nil() {
547        let mut interp = interpreter();
548
549        let s = interp.try_convert_mut("foo").unwrap();
550        let result = s
551            .funcall(&mut interp, "nil?", &[], None)
552            .and_then(|value| value.try_convert_into::<bool>(&interp));
553        let string_is_nil = unwrap_or_panic_with_backtrace(&mut interp, "Value::funcall", result);
554        assert!(!string_is_nil);
555    }
556
557    #[test]
558    #[cfg(feature = "core-regexp")]
559    fn funcall_string_split_regexp() {
560        let mut interp = interpreter();
561
562        let s = interp.try_convert_mut("foo").unwrap();
563        let delim = interp.try_convert_mut("").unwrap();
564        let result = s
565            .funcall(&mut interp, "split", &[delim], None)
566            .and_then(|value| value.try_convert_into_mut::<Vec<&str>>(&mut interp));
567        let split = unwrap_or_panic_with_backtrace(&mut interp, "Value::funcall", result);
568        assert_eq!(split, vec!["f", "o", "o"]);
569    }
570
571    #[test]
572    fn funcall_different_types() {
573        let mut interp = interpreter();
574        let nil = Value::nil();
575        let s = interp.try_convert_mut("foo").unwrap();
576        let result = nil
577            .funcall(&mut interp, "==", &[s], None)
578            .and_then(|value| value.try_convert_into::<bool>(&interp));
579        let eql = unwrap_or_panic_with_backtrace(&mut interp, "Value::funcall", result);
580        assert!(!eql);
581    }
582
583    #[test]
584    fn funcall_type_error() {
585        let mut interp = interpreter();
586        let nil = Value::nil();
587        let s = interp.try_convert_mut("foo").unwrap();
588        let err = s
589            .funcall(&mut interp, "+", &[nil], None)
590            .and_then(|value| value.try_convert_into_mut::<String>(&mut interp))
591            .unwrap_err();
592        assert_eq!("TypeError", err.name().as_ref());
593        assert_eq!(
594            b"no implicit conversion of nil into String".as_bstr(),
595            err.message().as_ref().as_bstr()
596        );
597    }
598
599    #[test]
600    fn funcall_method_not_exists() {
601        let mut interp = interpreter();
602        let nil = Value::nil();
603        let s = interp.try_convert_mut("foo").unwrap();
604        let err = nil.funcall(&mut interp, "garbage_method_name", &[s], None).unwrap_err();
605        assert_eq!("NoMethodError", err.name().as_ref());
606        assert_eq!(
607            b"undefined method 'garbage_method_name'".as_bstr(),
608            err.message().as_ref().as_bstr()
609        );
610    }
611
612    #[test]
613    fn value_respond_to() {
614        let mut interp = interpreter();
615        let nil = Value::nil();
616        assert!(nil.respond_to(&mut interp, "nil?").unwrap());
617        assert!(nil.respond_to(&mut interp, "class").unwrap());
618        assert!(!nil.respond_to(&mut interp, "zyxw_not_a_method").unwrap());
619
620        let object = interp.eval(b"Object.new").unwrap();
621        assert!(object.respond_to(&mut interp, "class").unwrap());
622        assert!(object.respond_to(&mut interp, "freeze").unwrap());
623        assert!(!object.respond_to(&mut interp, "zyxw_not_a_method").unwrap());
624
625        let array = interp.eval(b"[1, 2, 3]").unwrap();
626        assert!(array.respond_to(&mut interp, "class").unwrap());
627        assert!(array.respond_to(&mut interp, "length").unwrap());
628        assert!(!array.respond_to(&mut interp, "zyxw_not_a_method").unwrap());
629    }
630
631    #[test]
632    fn value_respond_to_basic_object() {
633        let mut interp = interpreter();
634
635        // `BasicObject` does not have `#respond_to?` and relies on
636        // `Value::respond_to` being implemented with VM method table lookups.
637        let basic_object = interp.eval(b"BasicObject.new").unwrap();
638        assert!(basic_object.respond_to(&mut interp, "__send__").unwrap());
639        assert!(!basic_object.respond_to(&mut interp, "class").unwrap());
640        assert!(!basic_object.respond_to(&mut interp, "zyxw_not_a_method").unwrap());
641    }
642
643    #[test]
644    fn arg_count_error_no_error_for_valid_count_values() {
645        // Construct a vector of Values with exactly the maximum allowed arguments.
646        let args = vec![Value::nil(); MRB_FUNCALL_ARGC_MAX];
647        // Expect conversion to fail, meaning no error should be constructed.
648        let err = ArgCountError::try_from(args.as_slice());
649        assert!(
650            err.is_err(),
651            "Expected no ArgCountError for {MRB_FUNCALL_ARGC_MAX} arguments",
652        );
653    }
654
655    #[test]
656    fn arg_count_error_error_for_excess_count_values() {
657        let count = MRB_FUNCALL_ARGC_MAX + 1;
658        let args = vec![Value::nil(); count];
659        // Expect conversion to succeed and produce an ArgCountError.
660        let err = ArgCountError::try_from(args.as_slice());
661        let arg_err = err.unwrap_or_else(|()| {
662            panic!("Expected an ArgCountError for {count} arguments, but conversion failed");
663        });
664        assert_eq!(arg_err.given, count);
665        assert_eq!(arg_err.max, MRB_FUNCALL_ARGC_MAX);
666    }
667
668    #[test]
669    fn arg_count_error_no_error_for_valid_count_sys_values() {
670        let args: Vec<sys::mrb_value> = vec![Value::nil().inner(); MRB_FUNCALL_ARGC_MAX];
671        // Expect conversion to fail for a valid sys value argument count.
672        let err = ArgCountError::try_from(args.as_slice());
673        assert!(
674            err.is_err(),
675            "Expected no ArgCountError for {MRB_FUNCALL_ARGC_MAX} sys values"
676        );
677    }
678
679    #[test]
680    fn arg_count_error_error_for_excess_count_sys_values() {
681        let count = MRB_FUNCALL_ARGC_MAX + 1;
682        let args: Vec<sys::mrb_value> = vec![Value::nil().inner(); count];
683        // Expect conversion to succeed and produce an ArgCountError for sys values.
684        let arg_err = ArgCountError::try_from(args.as_slice()).unwrap_or_else(|()| {
685            panic!("Expected an ArgCountError for {count} sys values, but conversion failed");
686        });
687        assert_eq!(arg_err.given, count);
688        assert_eq!(arg_err.max, MRB_FUNCALL_ARGC_MAX);
689    }
690
691    #[test]
692    fn arg_count_error_display_format() {
693        let given = MRB_FUNCALL_ARGC_MAX + 2;
694        let error = ArgCountError {
695            given,
696            max: MRB_FUNCALL_ARGC_MAX,
697        };
698        let display = format!("{error}");
699        let expected = format!(
700            "Too many arguments for function call: gave {given} arguments, but Artichoke only supports a maximum of {MRB_FUNCALL_ARGC_MAX} arguments",
701        );
702        assert_eq!(display, expected);
703    }
704
705    #[test]
706    fn integration_funcall_with_excess_arguments() {
707        let mut interp = interpreter();
708        let nil = Value::nil();
709        let args = vec![Value::nil(); MRB_FUNCALL_ARGC_MAX + 1];
710        let result = nil.funcall(&mut interp, "nil?", &args, None);
711        let err = result.expect_err("Expected an error due to too many arguments, but funcall succeeded");
712        // ArgCountError is converted into an Error with name "ArgumentError".
713        assert_eq!(err.name().as_ref(), "ArgumentError");
714        let message = err.message();
715        assert!(
716            message.as_bstr().contains_str("Too many arguments"),
717            "Error message does not indicate too many arguments"
718        );
719    }
720}