mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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]
This commit is contained in:
parent
b3aff6a11c
commit
878af5147d
Notes:
git
2020-06-20 04:58:52 +09:00
5 changed files with 93 additions and 9 deletions
62
proc.c
62
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);
|
||||
|
|
|
@ -2,5 +2,11 @@ require_relative '../../spec_helper'
|
|||
require_relative 'shared/equal'
|
||||
|
||||
describe "Proc#eql?" do
|
||||
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
|
||||
|
|
|
@ -2,5 +2,11 @@ require_relative '../../spec_helper'
|
|||
require_relative 'shared/equal'
|
||||
|
||||
describe "Proc#==" do
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue