diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 0eb47062c9f8d0..49a3ccb08d593b 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1334,16 +1334,77 @@ pub mod test_utils { #[cfg(test)] pub use test_utils::*; -/// Get class name from a class pointer. +/// Get class name from a class pointer. For anonymous classes, includes the +/// superclass name for context (e.g. `#`). pub fn get_class_name(class: VALUE) -> String { // type checks for rb_class2name() - if unsafe { RB_TYPE_P(class, RUBY_T_MODULE) || RB_TYPE_P(class, RUBY_T_CLASS) } { + let name = if unsafe { RB_TYPE_P(class, RUBY_T_MODULE) || RB_TYPE_P(class, RUBY_T_CLASS) } { Some(class) } else { None }.and_then(|class| unsafe { cstr_to_rust_string(rb_class2name(class)) - }).unwrap_or_else(|| "Unknown".to_string()) + }).unwrap_or_else(|| "Unknown".to_string()); + + // For anonymous classes, include the superclass name for context. + // Use rb_class_real to resolve through iclasses (internal include/prepend + // wrappers) before checking rb_mod_name, which returns Qnil for anonymous classes. + // e.g. "#" with superclass String => "#" + if unsafe { RB_TYPE_P(class, RUBY_T_CLASS) && rb_mod_name(rb_class_real(class)) == Qnil } { + let super_class = unsafe { rb_class_get_superclass(class) }; + if super_class != Qnil { + let super_name = get_class_name(super_class); + return format!("#", class.0); + } + } + + name +} + + +#[cfg(test)] +mod class_name_tests { + use super::*; + use test_utils::{eval, with_rubyvm}; + + #[test] + fn named_class() { + with_rubyvm(|| { + assert_eq!(get_class_name(eval("String")), "String"); + }); + } + + #[test] + fn named_module() { + with_rubyvm(|| { + assert_eq!(get_class_name(eval("Kernel")), "Kernel"); + }); + } + + #[test] + fn anonymous_class_includes_superclass() { + with_rubyvm(|| { + let name = get_class_name(eval("Class.new(String)")); + assert!(name.starts_with("# bool {