Bug #9005

object.send(:define_method, ...){...} creates private method

Added by Jeremy Evans over 1 year ago. Updated 5 months ago.

[ruby-core:57747]
Status:Closed
Priority:Normal
Assignee:Nobuyoshi Nakada
ruby -v:ruby 2.1.0dev (2013-09-22 trunk 43011) [i386-openbsd] Backport:1.9.3: DONTNEED, 2.0.0: DONTNEED, 2.1.0: REQUIRED

Description

I assume this is caused by r40022, which made define_method consider visibility. However, visibility should only be considered if define_method is called normally, not via send. When called via send, it should define a public method. Here's example code showing the error:

$ ruby21 -ve "Object.send(:define_method, :foo){|*a| 1}.foo"
-e:1:in <main>': private methodfoo' called for :foo:Symbol (NoMethodError)

I apologize in advance if this has already been fixed (I tested 2.1.0-preview1, not trunk), but from the commit logs it doesn't appear to have been.


Related issues

Related to Backport21 - Backport #9296: please backport r44380 (visibility of methods defined by define_method) Closed 12/25/2013
Duplicated by Ruby trunk - Bug #9141: define_singleton_method creates private method Closed 11/22/2013

Associated revisions

Revision 44380
Added by Nobuyoshi Nakada over 1 year ago

proc.c: make method by define_method public

  • proc.c (rb_mod_define_method): consider visibility only if self in the caller is same as the receiver, otherwise make public as well as old behavior. [Bug #9005] [Bug #9141]
  • vm.c (rb_vm_cref_in_context): return ruby level cref if self is same.

Revision 44380
Added by Nobuyoshi Nakada over 1 year ago

proc.c: make method by define_method public

  • proc.c (rb_mod_define_method): consider visibility only if self in the caller is same as the receiver, otherwise make public as well as old behavior. [Bug #9005] [Bug #9141]
  • vm.c (rb_vm_cref_in_context): return ruby level cref if self is same.

History

#1 Updated by Evan Light over 1 year ago

Ran against trunk:

-e:1:in <main>': private methodfoo' called for :foo:Symbol (NoMethodError)

#2 Updated by Nobuyoshi Nakada over 1 year ago

  • Status changed from Open to Assigned
  • Assignee set to Nobuyoshi Nakada

#3 Updated by Jeremy Evans over 1 year ago

There's only two days until the release of 2.1.0, and this still hasn't been fixed. This is a serious regression that breaks existing code (among other things, it causes Sequel's tests to freeze). If this can't be fixed before 2.1.0, I think r40022 should be reverted on the 2.1 branch, and the feature should be moved to 2.2.0. Assuming this is fixed on trunk before 2.1.0, it should be immediately backported to the 2.1 branch.

#4 Updated by Nobuyoshi Nakada over 1 year ago

=begin
(({send})) is irrelevant here.
If you make (({define_method})) public, it isn't needed.
c = Class.new {
class << self
public :define_method
end
}
c.define_method(:foo) {}
c.new.foo #=> NoMethodError

Your code just reflects the default visibility at the top level.
=end

#5 Updated by Nobuyoshi Nakada over 1 year ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r44380.
Jeremy, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


proc.c: make method by define_method public

  • proc.c (rb_mod_define_method): consider visibility only if self in the caller is same as the receiver, otherwise make public as well as old behavior. [Bug #9005] [Bug #9141]
  • vm.c (rb_vm_cref_in_context): return ruby level cref if self is same.

#6 Updated by Nobuyoshi Nakada over 1 year ago

  • Backport set to 1.9.3: DONTNEED, 2.0.0: DONTNEED, 2.1.0: REQUIRED

#7 Updated by Scott Tamosunas 5 months ago

This isn't fixed on 2.1.3. Has that changeset not made it into a release?

2.1.3 :001 > RUBY_VERSION
=> "2.1.3"
2.1.3 :002 > class Foo
2.1.3 :003?>
2.1.3 :004 > private
2.1.3 :005?>
2.1.3 :006 > define_method("bar") { puts "BAR" }
2.1.3 :007?> end
=> :bar
2.1.3 :008 >
2.1.3 :009 > Foo.new.bar
NoMethodError: private method
bar' called for #Foo:0x007ff90b8e3198
from (irb):9
from /Users/me/.rvm/rubies/ruby-2.1.3/bin/irb:11:in <main>'

Where this works on 2.0

2.0.0-p451 :001 > RUBY_VERSION
=> "2.0.0"
2.0.0-p451 :002 > class Foo
2.0.0-p451 :003?>
2.0.0-p451 :004 > private
2.0.0-p451 :005?>
2.0.0-p451 :006 > define_method("bar") { puts "BAR" }
2.0.0-p451 :007?> end
=> #<Proc:0x0000010103cd00@(irb):6 (lambda)>
2.0.0-p451 :008 >
2.0.0-p451 :009 > Foo.new.bar
BAR

#8 Updated by Jeremy Evans 5 months ago

Scott Tamosunas wrote:

This isn't fixed on 2.1.3. Has that changeset not made it into a release?

2.1.3 :001 > RUBY_VERSION
=> "2.1.3"
2.1.3 :002 > class Foo
2.1.3 :003?>
2.1.3 :004 > private
2.1.3 :005?>
2.1.3 :006 > define_method("bar") { puts "BAR" }
2.1.3 :007?> end
=> :bar
2.1.3 :008 >
2.1.3 :009 > Foo.new.bar
NoMethodError: private method
bar' called for #Foo:0x007ff90b8e3198
from (irb):9
from /Users/me/.rvm/rubies/ruby-2.1.3/bin/irb:11:in <main>'

Where this works on 2.0

2.0.0-p451 :001 > RUBY_VERSION
=> "2.0.0"
2.0.0-p451 :002 > class Foo
2.0.0-p451 :003?>
2.0.0-p451 :004 > private
2.0.0-p451 :005?>
2.0.0-p451 :006 > define_method("bar") { puts "BAR" }
2.0.0-p451 :007?> end
=> #<Proc:0x0000010103cd00@(irb):6 (lambda)>
2.0.0-p451 :008 >
2.0.0-p451 :009 > Foo.new.bar
BAR

The behavior change here is deliberate, since you are calling define_method inside the class definition after calling private. This bug was that define_method when called outside the class definition was generating private methods, which was fixed before the release of 2.1.0.

Also available in: Atom PDF