mruby-class-ext: migrate comparison methods to c

The following Module methods were migrated from Ruby to C:

   - `<`
   - `<=`
   - `>`
   - `>=`
   - `<=>`

Co-authored-by: Gemini <gemini@google.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-07-04 08:15:30 +09:00
parent 9ca5ff2f74
commit 815b34c509
2 changed files with 190 additions and 89 deletions
-89
View File
@@ -1,89 +0,0 @@
class Module
##
# call-seq:
# mod < other -> true, false, or nil
#
# Returns true if `mod` is a subclass of `other`. Returns
# <code>nil</code> if there's no relationship between the two.
# (Think of the relationship in terms of the class definition:
# "class A < B" implies "A < B".)
#
def <(other)
if self.equal?(other)
false
else
self <= other
end
end
##
# call-seq:
# mod <= other -> true, false, or nil
#
# Returns true if `mod` is a subclass of `other` or
# is the same as `other`. Returns
# <code>nil</code> if there's no relationship between the two.
# (Think of the relationship in terms of the class definition:
# "class A < B" implies "A < B".)
def <=(other)
raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
if self.ancestors.include?(other)
return true
elsif other.ancestors.include?(self)
return false
end
end
##
# call-seq:
# mod > other -> true, false, or nil
#
# Returns true if `mod` is an ancestor of `other`. Returns
# <code>nil</code> if there's no relationship between the two.
# (Think of the relationship in terms of the class definition:
# "class A < B" implies "B > A".)
#
def >(other)
if self.equal?(other)
false
else
self >= other
end
end
##
# call-seq:
# mod >= other -> true, false, or nil
#
# Returns true if `mod` is an ancestor of `other`, or the
# two modules are the same. Returns
# <code>nil</code> if there's no relationship between the two.
# (Think of the relationship in terms of the class definition:
# "class A < B" implies "B > A".)
#
def >=(other)
raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
return other < self
end
##
# call-seq:
# module <=> other_module -> -1, 0, +1, or nil
#
# Comparison---Returns -1, 0, +1 or nil depending on whether `module`
# includes `other_module`, they are the same, or if `module` is included by
# `other_module`.
#
# Returns `nil` if `module` has no relationship with `other_module`, if
# `other_module` is not a module, or if the two values are incomparable.
#
def <=>(other)
return 0 if self.equal?(other)
return nil unless other.is_a?(Module)
cmp = self < other
return -1 if cmp
return 1 unless cmp.nil?
return nil
end
end
+190
View File
@@ -121,16 +121,206 @@ class_attached_object(mrb_state *mrb, mrb_value self)
return mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__attached__));
}
/*
* Check if a class/module is an ancestor of another.
*
* This function traverses the inheritance chain of `klass` upwards to determine
* if `super` appears anywhere in the hierarchy. It handles both regular classes/modules
* and included classes (ICLASS) which represent modules included in the inheritance chain.
*
* Args:
* klass: The class/module to check (potential descendant)
* super: The class/module to search for (potential ancestor)
*
* Returns:
* true: if `super` is found in `klass`'s inheritance chain
* false: if `super` is not an ancestor of `klass`
*/
static mrb_bool
is_ancestor(struct RClass *klass, struct RClass *super)
{
struct RClass *c = klass;
while (c) {
if (c->tt == MRB_TT_ICLASS) {
if (c->c == super) return TRUE;
}
else {
if (c == super) return TRUE;
}
c = c->super;
}
return FALSE;
}
/*
* Compare hierarchy relationship between two modules/classes.
*
* This function determines the ancestor relationship between `self` and `other`.
* It checks if one is an ancestor of the other by traversing the inheritance chain.
*
* Args:
* mrb: The mruby state
* self: The first module/class to compare
* other: The second module/class to compare
*
* Returns:
* true: if `self` is an ancestor of `other` (self > other)
* false: if `other` is an ancestor of `self` (self < other)
* nil: if there's no inheritance relationship between them
*
* Raises:
* TypeError: if `other` is not a class, module, or included class
*/
static mrb_value
mod_compare_hierarchy(mrb_state *mrb, mrb_value self, mrb_value other)
{
if (!mrb_class_p(other) && !mrb_module_p(other) && !mrb_iclass_p(other)) {
mrb_raise(mrb, E_TYPE_ERROR, "compared with non class/module");
}
struct RClass *self_c = mrb_class_ptr(self);
struct RClass *other_c = mrb_class_ptr(other);
if (is_ancestor(self_c, other_c)) {
return mrb_true_value();
}
if (is_ancestor(other_c, self_c)) {
return mrb_false_value();
}
return mrb_nil_value();
}
/*
* call-seq:
* mod <= other -> true, false, or nil
*
* Returns true if mod is a subclass of other or is the same as other.
* Returns nil if there's no relationship between the two.
*/
static mrb_value
mrb_mod_le(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_get_args(mrb, "o", &other);
return mod_compare_hierarchy(mrb, self, other);
}
/*
* call-seq:
* mod < other -> true, false, or nil
*
* Returns true if mod is a subclass of other. Returns false if mod
* is the same as other. Returns nil if there's no relationship between the two.
*/
static mrb_value
mrb_mod_lt(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_get_args(mrb, "o", &other);
if (mrb_obj_equal(mrb, self, other)) {
return mrb_false_value();
}
return mod_compare_hierarchy(mrb, self, other);
}
/*
* call-seq:
* mod >= other -> true, false, or nil
*
* Returns true if mod is an ancestor of other, or the two modules are the same.
* Returns nil if there's no relationship between the two.
*/
static mrb_value
mrb_mod_ge(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_get_args(mrb, "o", &other);
return mod_compare_hierarchy(mrb, other, self);
}
/*
* call-seq:
* mod > other -> true, false, or nil
*
* Returns true if mod is an ancestor of other. Returns false if mod
* is the same as other. Returns nil if there's no relationship between the two.
*/
static mrb_value
mrb_mod_gt(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_get_args(mrb, "o", &other);
if (mrb_obj_equal(mrb, self, other)) {
return mrb_false_value();
}
return mod_compare_hierarchy(mrb, other, self);
}
/*
* call-seq:
* module <=> other_module -> -1, 0, +1, or nil
*
* Comparison - Returns -1, 0, +1 or nil depending on whether module
* includes other_module, they are the same, or if module is included by
* other_module.
*
* Returns nil if module has no relationship with other_module, if
* other_module is not a module, or if the two values are incomparable.
*/
static mrb_value
mrb_mod_cmp(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_get_args(mrb, "o", &other);
if (mrb_obj_equal(mrb, self, other)) {
return mrb_fixnum_value(0);
}
if (!mrb_class_p(other) && !mrb_module_p(other) && !mrb_iclass_p(other)) {
return mrb_nil_value();
}
mrb_value cmp = mod_compare_hierarchy(mrb, self, other);
if (mrb_true_p(cmp)) {
return mrb_fixnum_value(-1);
}
else if (mrb_false_p(cmp)) {
return mrb_fixnum_value(1);
}
else {
return mrb_nil_value();
}
}
/*
* Initialize the mruby-class-ext gem.
*
* This function registers all the extension methods to the Module and Class classes.
* It's called automatically when the gem is loaded.
*
* Args:
* mrb: The mruby state
*/
void
mrb_mruby_class_ext_gem_init(mrb_state *mrb)
{
struct RClass *mod = mrb->module_class;
/* Module methods */
mrb_define_method_id(mrb, mod, MRB_SYM(name), mod_name, MRB_ARGS_NONE());
mrb_define_method_id(mrb, mod, MRB_SYM_Q(singleton_class), mod_singleton_class_p, MRB_ARGS_NONE());
mrb_define_method_id(mrb, mod, MRB_SYM(module_exec), mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK());
mrb_define_method_id(mrb, mod, MRB_SYM(class_exec), mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK());
/* Module comparison operators */
mrb_define_method_id(mrb, mod, MRB_OPSYM(lt), mrb_mod_lt, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, mod, MRB_OPSYM(le), mrb_mod_le, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, mod, MRB_OPSYM(gt), mrb_mod_gt, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, mod, MRB_OPSYM(ge), mrb_mod_ge, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, mod, MRB_OPSYM(cmp), mrb_mod_cmp, MRB_ARGS_REQ(1));
/* Class-specific methods */
struct RClass *cls = mrb->class_class;
mrb_define_method_id(mrb, cls, MRB_SYM(subclasses), class_subclasses, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(attached_object), class_attached_object, MRB_ARGS_NONE());