summaryrefslogtreecommitdiff
path: root/proc.c
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2020-06-01 12:28:05 -0700
committerJeremy Evans <code@jeremyevans.net>2020-06-19 12:58:25 -0700
commit878af5147def7fed089d3cc388742f0111db58ae (patch)
tree93e6e68b6b40575f7a1f3a641383c4111f05ef08 /proc.c
parentb3aff6a11cbc96e5fc6c615d3f7a7a11fda6f59a (diff)
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]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3174
Diffstat (limited to 'proc.c')
-rw-r--r--proc.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/proc.c b/proc.c
index 4af42c04bd..82b48dff9f 100644
--- a/proc.c
+++ b/proc.c
@@ -1255,6 +1255,66 @@ rb_proc_get_iseq(VALUE self, int *is_proc)
}
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)
{
VALUE loc[2];
@@ -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);