From 878af5147def7fed089d3cc388742f0111db58ae Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Mon, 1 Jun 2020 12:28:05 -0700 Subject: Implement Proc#== and #eql? Previously, these were not implemented, and Object#== and #eql? were used. This tries to check the proc internals to make sure that procs created from separate blocks are treated as not equal, but procs created from the same block are treated as equal, even when the lazy proc allocation optimization is used. Implements [Feature #14267] --- proc.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'proc.c') diff --git a/proc.c b/proc.c index 4af42c04bd..82b48dff9f 100644 --- a/proc.c +++ b/proc.c @@ -1254,6 +1254,66 @@ rb_proc_get_iseq(VALUE self, int *is_proc) return NULL; } +static VALUE +proc_eq(VALUE self, VALUE other) +{ + const rb_proc_t *self_proc, *other_proc; + const struct rb_block *self_block, *other_block; + const struct rb_captured_block *self_cblock, *other_cblock; + + if (rb_obj_class(self) != rb_obj_class(other)) { + return Qfalse; + } + + GetProcPtr(self, self_proc); + GetProcPtr(other, other_proc); + + if (self_proc->is_from_method != other_proc->is_from_method || + self_proc->is_lambda != other_proc->is_lambda) { + return Qfalse; + } + + self_block = &self_proc->block; + other_block = &other_proc->block; + + if (vm_block_type(self_block) != vm_block_type(other_block)) { + return Qfalse; + } + + switch (vm_block_type(self_block)) { + case block_type_iseq: + if (self_block->as.captured.ep != \ + other_block->as.captured.ep || + self_block->as.captured.code.iseq != \ + other_block->as.captured.code.iseq) { + return Qfalse; + + } + break; + case block_type_ifunc: + if (self_block->as.captured.ep != \ + other_block->as.captured.ep || + self_block->as.captured.code.ifunc != \ + other_block->as.captured.code.ifunc) { + return Qfalse; + + } + break; + case block_type_proc: + if (self_block->as.proc != other_block->as.proc) { + return Qfalse; + } + break; + case block_type_symbol: + if (self_block->as.symbol != other_block->as.symbol) { + return Qfalse; + } + break; + } + + return Qtrue; +} + static VALUE iseq_location(const rb_iseq_t *iseq) { @@ -3970,6 +4030,8 @@ Init_Proc(void) rb_define_method(rb_cProc, "curry", proc_curry, -1); rb_define_method(rb_cProc, "<<", proc_compose_to_left, 1); rb_define_method(rb_cProc, ">>", proc_compose_to_right, 1); + rb_define_method(rb_cProc, "==", proc_eq, 1); + rb_define_method(rb_cProc, "eql?", proc_eq, 1); rb_define_method(rb_cProc, "source_location", rb_proc_location, 0); rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0); rb_define_method(rb_cProc, "ruby2_keywords", proc_ruby2_keywords, 0); -- cgit v1.2.3