From: "jeremyevans0 (Jeremy Evans) via ruby-core" Date: 2023-06-28T15:27:16+00:00 Subject: [ruby-core:114044] [Ruby master Bug#19749] Confirm correct behaviour when attaching private method with `#define_method` Issue #19749 has been updated by jeremyevans0 (Jeremy Evans). Eregon (Benoit Daloze) wrote in #note-3: > @jeremyevans0 That does not explain why the original example defines the method as public (note I edited the description on Redmine, it was not asking the right question). > > > will only consider current default scope visibility to determine visibility: > > And that visibility is private at the top-level. So why is the method defined as public on CRuby? Scope visibility is only used if the receiver of the method is the same as current scope, which was not the case in the previous examples given. `define_method` at top level does not define private methods. I think this is because the receiver of the `define_method` is `main`, but the method is defined in `Object`, so the scope visibility does not apply. I don't think this is a bug, see #9005. However, I think there is a bug here, in that `define_method` will not change the existing visibility of a method if `define_method` is called with the current method body: ```ruby def bar; end define_method(:bar, method(:bar)) Object.new.bar # NoMethodError ``` This is not related to top-level: ```ruby class Foo def bar; end define_method(:bar, method(:bar)) new.bar # NoMethodError end ``` Note that `define_method` does not copy the method body visibility if there is not an existing method entry: ```ruby def bar; end define_method(:baz, method(:bar)) Object.new.baz # no error ``` It will override the method visibility if the method is already defined but does not have the same implementation, but in that case it will not use the method body visibility: ```ruby def bar; end def baz; end define_method(:baz, method(:bar)) Object.new.baz # no error ``` This also affects `define_singleton_method`: ```ruby foo = Object.new foo.singleton_class.class_exec do private def bar; end end foo.define_singleton_method(:bar, foo.method(:bar)) foo.bar # NoMethodError ``` I think `define_method`/`define_singleton_method` should be changed to reset the method visibility if the current method body is passed, either to the scope visibility if the receiver is the same as the current scope, or to public otherwise. ---------------------------------------- Bug #19749: Confirm correct behaviour when attaching private method with `#define_method` https://bugs.ruby-lang.org/issues/19749#change-103708 * Author: itarato (Peter Arato) * Status: Open * Priority: Normal * ruby -v: 3.3.0 * Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- This issue is a special case of https://bugs.ruby-lang.org/issues/19745: Should dynamically added private methods via `.singleton_class.send(:define_method,...` at the top-level be accessible publicly? See the following example: ```ruby def bar; end foo = Object.new foo.singleton_class.define_method(:bar, method(:bar)) foo.bar # No error. ``` The script above runs fine on latest Ruby 3.3. Is this correct to ignore the fact that the visibility in the caller context is the default top-level private visibility? This came up during a TruffleRuby investigation (https://github.com/oracle/truffleruby/issues/3134) where the result for the same script is: `private method 'bar' called for # (NoMethodError)` -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/