diff options
-rw-r--r-- | proc.c | 62 | ||||
-rw-r--r-- | spec/ruby/core/proc/eql_spec.rb | 8 | ||||
-rw-r--r-- | spec/ruby/core/proc/equal_value_spec.rb | 8 | ||||
-rw-r--r-- | spec/ruby/core/proc/shared/equal.rb | 14 | ||||
-rw-r--r-- | test/ruby/test_proc.rb | 10 |
5 files changed, 93 insertions, 9 deletions
@@ -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); diff --git a/spec/ruby/core/proc/eql_spec.rb b/spec/ruby/core/proc/eql_spec.rb index a3c5a5658d..d95e890c29 100644 --- a/spec/ruby/core/proc/eql_spec.rb +++ b/spec/ruby/core/proc/eql_spec.rb @@ -2,5 +2,11 @@ require_relative '../../spec_helper' require_relative 'shared/equal' describe "Proc#eql?" do - it_behaves_like :proc_equal_undefined, :eql? + ruby_version_is "0"..."2.8" do + it_behaves_like :proc_equal_undefined, :eql? + end + + ruby_version_is "2.8" do + it_behaves_like :proc_equal, :eql? + end end diff --git a/spec/ruby/core/proc/equal_value_spec.rb b/spec/ruby/core/proc/equal_value_spec.rb index 1b6ac792cf..fb465992e9 100644 --- a/spec/ruby/core/proc/equal_value_spec.rb +++ b/spec/ruby/core/proc/equal_value_spec.rb @@ -2,5 +2,11 @@ require_relative '../../spec_helper' require_relative 'shared/equal' describe "Proc#==" do - it_behaves_like :proc_equal_undefined, :== + ruby_version_is "0"..."2.8" do + it_behaves_like :proc_equal_undefined, :== + end + + ruby_version_is "2.8" do + it_behaves_like :proc_equal, :== + end end diff --git a/spec/ruby/core/proc/shared/equal.rb b/spec/ruby/core/proc/shared/equal.rb index 46a1894424..0c0020ca7f 100644 --- a/spec/ruby/core/proc/shared/equal.rb +++ b/spec/ruby/core/proc/shared/equal.rb @@ -36,26 +36,26 @@ describe :proc_equal, shared: true do a.send(@method, b).should be_false end - it "returns true if both procs have the same body and environment" do + it "returns false if procs are distinct but have the same body and environment" do p = proc { :foo } p2 = proc { :foo } - p.send(@method, p2).should be_true + p.send(@method, p2).should be_false end - it "returns true if both lambdas with the same body and environment" do + it "returns false if lambdas are distinct but have same body and environment" do x = -> { :foo } x2 = -> { :foo } - x.send(@method, x2).should be_true + x.send(@method, x2).should be_false end - it "returns true if both different kinds of procs with the same body and env" do + it "returns false if using comparing lambda to proc, even with the same body and env" do p = -> { :foo } p2 = proc { :foo } - p.send(@method, p2).should be_true + p.send(@method, p2).should be_false x = proc { :bar } x2 = -> { :bar } - x.send(@method, x2).should be_true + x.send(@method, x2).should be_false end it "returns false if other is not a Proc" do diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 765b400dbe..0cc5d488c2 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -137,6 +137,14 @@ class TestProc < Test::Unit::TestCase lambda { x } end + def m_nest0(&block) + block + end + + def m_nest(&block) + [m_nest0(&block), m_nest0(&block)] + end + def test_eq a = m(1) b = m(2) @@ -148,6 +156,8 @@ class TestProc < Test::Unit::TestCase a = lambda {|x| lambda {} }.call(1) b = lambda {} assert_not_equal(a, b, "[ruby-dev:22601]") + + assert_equal(*m_nest{}, "[ruby-core:84583] Feature #14627") end def test_block_par |