Details

    • Type: Bug Bug
    • Status: Resolved Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: JRuby 1.7.0.pre2
    • Fix Version/s: JRuby 1.7.0.RC1
    • Component/s: None
    • Labels:
      None
    • Environment:
      MacOS 10.7.3, Netbeans 7.2
    • Testcase included:
      yes
    • Number of attachments :
      0

      Description

      There seems to be an issue with a combination of public_send() (or send()) and
      method_missing() when trying to call a method named "warn". The issue appears
      with JRuby, but not when running the same code in Ruby from the command line.

      First, the test case:

      JUnit Test Case
         @Test
          public void shouldRunFine() {
      
              ScriptingContainer container = new ScriptingContainer();
      
              container.runScriptlet(
      
                 "class Me                                            \n" +
                     "def method_missing(method_name, *args, &block)  \n" +
                         "puts \"you have called: #{method_name}\"    \n" +
                     "end                                             \n" +
                 "end                                                 \n" +
      
                 "m = Me.new                                          \n" +
                 "m.public_send(:info)                                \n" +
                 "m.public_send(:blah)                                \n" +
      
                 "# problem happens just here:                        \n" +
                 "m.public_send(:warn)                                \n"
              );
          }
      

      As above the code throws an exception on last line, output:

      ------------- Standard Output ---------------
      you have called: info
      you have called: blah
      ------------- ---------------- ---------------
      ------------- Standard Error -----------------
      NoMethodError: private method `warn' called for Me:Class
        public_send at org/jruby/RubyKernel.java:1784
             (root) at <script>:10
      ------------- ---------------- ---------------
      Testcase: shouldRunFine(org.magnepath.general.RubyCallerTest):  Caused an ERROR
      (NoMethodError) private method `warn' called for Me:Class
      org.jruby.embed.EvalFailedException: (NoMethodError) private method `warn' called for Me:Class
          at org.jruby.embed.internal.EmbedEvalUnitImpl.run(EmbedEvalUnitImpl.java:133)
          at org.jruby.embed.ScriptingContainer.runUnit(ScriptingContainer.java:1264)
          at org.jruby.embed.ScriptingContainer.runScriptlet(ScriptingContainer.java:1257)
          at org.magnepath.general.RubyCallerTest.shouldRunFine(RubyCallerTest.java:80)
      Caused by: org.jruby.exceptions.RaiseException: (NoMethodError) private method `warn' called for Me:Class
          at org.jruby.RubyKernel.public_send(org/jruby/RubyKernel.java:1784)
          at RUBY.(root)(<script>:10)
      

      The failure happens on script line 10, which is the attempted call to "warn".
      The other invovations of public_send() work as expected.

      If I change the final line to use "send(:warn)" instead of public_send(), I get:

      ------------- Standard Output ---------------
      you have called: info
      you have called: blah
      ------------- ---------------- ---------------
      ------------- Standard Error -----------------
      ArgumentError: wrong number of arguments calling `warn` (0 for 1)
        __send__ at org/jruby/RubyBasicObject.java:1667
            send at org/jruby/RubyKernel.java:2060
          (root) at <script>:10
      ------------- ---------------- ---------------
      Testcase: shouldRunFine(org.magnepath.general.RubyCallerTest):  Caused an ERROR
      (ArgumentError) wrong number of arguments calling `warn` (0 for 1)
      org.jruby.embed.EvalFailedException: (ArgumentError) wrong number of arguments calling `warn` (0 for 1)
          at org.jruby.embed.internal.EmbedEvalUnitImpl.run(EmbedEvalUnitImpl.java:133)
          at org.jruby.embed.ScriptingContainer.runUnit(ScriptingContainer.java:1264)
          at org.jruby.embed.ScriptingContainer.runScriptlet(ScriptingContainer.java:1257)
          at org.magnepath.general.RubyCallerTest.shouldRunFine(RubyCallerTest.java:80)
      Caused by: org.jruby.exceptions.RaiseException: (ArgumentError) wrong number of arguments calling `warn` (0 for 1)
          at org.jruby.RubyBasicObject.__send__(org/jruby/RubyBasicObject.java:1667)
          at org.jruby.RubyKernel.send(org/jruby/RubyKernel.java:2060)
          at RUBY.(root)(<script>:10)
      

      which seems to suggest their is a private :warn method on Class with 1 argument?

      Meanwhile, if I run the equivalent code as below

      class Me
          def method_missing(method_name, *args, &block)
              puts "you have called: #{method_name}"
          end
      end
      
      m = Me.new
      m.public_send(:info)
      m.public_send(:blah)
      m.public_send(:warn)
      

      against Ruby on the command-line, I get the following output with no errors:

      you have called: info
      you have called: blah
      you have called: warn
      

      The version of ruby installed on my system is:

      ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.3.0]
      

        Activity

        Hide
        nick added a comment -

        Apologies, I appear to have screwed up the formatting quite badly, and don't have the permission to edit it.

        If someone would like to fix, please go ahead.

        Show
        nick added a comment - Apologies, I appear to have screwed up the formatting quite badly, and don't have the permission to edit it. If someone would like to fix, please go ahead.
        Hide
        Hiro Asari added a comment -

        We are probably checking the method visibility even when method_missing doesn't call for it.

        With MRI, method visibility is checked only when necessary.

        1.9.3p194 :001 > RUBY_DESCRIPTION
         => "ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin12.0.0]" 
        1.9.3p194 :002 > class M; def method_missing(sym, *args, &blk); super; end; end
         => nil 
        1.9.3p194 :003 > M.new.public_send :puts, "hello?"
        NoMethodError: private method `puts' called for #<M:0x007fc8448b0268>
        	from (irb):2:in `method_missing'
        	from (irb):3:in `public_send'
        	from (irb):3
        	from /Users/asari/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
        

        If you override method_missing, there won't be a NoMethodError:

        1.9.3p194 :004 > class M; def method_missing(sym, *args, &blk); end; end
         => nil 
        1.9.3p194 :005 > M.new.public_send :puts, "hello?"
         => nil 
        
        Show
        Hiro Asari added a comment - We are probably checking the method visibility even when method_missing doesn't call for it. With MRI, method visibility is checked only when necessary. 1.9.3p194 :001 > RUBY_DESCRIPTION => "ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin12.0.0]" 1.9.3p194 :002 > class M; def method_missing(sym, *args, &blk); super; end; end => nil 1.9.3p194 :003 > M.new.public_send :puts, "hello?" NoMethodError: private method `puts' called for #<M:0x007fc8448b0268> from (irb):2:in `method_missing' from (irb):3:in `public_send' from (irb):3 from /Users/asari/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>' If you override method_missing , there won't be a NoMethodError : 1.9.3p194 :004 > class M; def method_missing(sym, *args, &blk); end; end => nil 1.9.3p194 :005 > M.new.public_send :puts, "hello?" => nil
        Hide
        nick added a comment -

        Hi Hiro,

        Thanks for the quick response, and for fixing up my formatting!

        Re: your comment: great point, thank you for that.

        Unless I'm confused, there does appear to be something particular in my example about sending the message "warn", while sending the messages "info" and "blah" worked fine – since when I tried changing it to a send() message it reported an error about wrong number of arguments, which looked strange to me. Perhaps there's more than one issue here?

        (In case its useful here's some context on how I came across this: I'm using the Ruby Dicom library, version 0.9.3. Its logging.rb uses the method_missing() to implement a proxy logger, and the read() method of d_object.rb uses public_send() to log. While experimenting I noticed that "debug" messages were logged fine, but an attempt to log a "warn" resulted in an exception.)

        thanks again,

        nick

        Show
        nick added a comment - Hi Hiro, Thanks for the quick response, and for fixing up my formatting! Re: your comment: great point, thank you for that. Unless I'm confused, there does appear to be something particular in my example about sending the message "warn", while sending the messages "info" and "blah" worked fine – since when I tried changing it to a send() message it reported an error about wrong number of arguments, which looked strange to me. Perhaps there's more than one issue here? (In case its useful here's some context on how I came across this: I'm using the Ruby Dicom library, version 0.9.3. Its logging.rb uses the method_missing() to implement a proxy logger, and the read() method of d_object.rb uses public_send() to log. While experimenting I noticed that "debug" messages were logged fine, but an attempt to log a "warn" resulted in an exception.) thanks again, nick
        Hide
        Charles Oliver Nutter added a comment -

        The actual problem here is pretty simple: we raise an error unconditionally when doing public_send against a private method, rather than redispatching to method_missing.

        I have a fix, testing it now.

        Show
        Charles Oliver Nutter added a comment - The actual problem here is pretty simple: we raise an error unconditionally when doing public_send against a private method, rather than redispatching to method_missing. I have a fix, testing it now.
        Hide
        Charles Oliver Nutter added a comment -
        commit 09316871276d9dd9cec5ba355d92eec525b16c83
        Author: Charles Oliver Nutter <headius@headius.com>
        Date:   Wed Sep 19 03:23:11 2012 -0500
        
            Fix JRUBY-6885
            
            method_missing and private methods behave differently
            
            public_send was unconditionally raising an error if the requested
            name was defined, but not public. Modified to redispatch to
            method_missing.
        
        :000000 100644 0000000... 48866d0... A	spec/regression/JRUBY-6885_public_send_calls_method_missing_for_privates.rb
        :100644 100644 dc354fd... a3f36d4... M	src/org/jruby/RubyKernel.java
        :100644 100644 ad2154a... 643d7d1... M	src/org/jruby/RubyModule.java
        
        Show
        Charles Oliver Nutter added a comment - commit 09316871276d9dd9cec5ba355d92eec525b16c83 Author: Charles Oliver Nutter <headius@headius.com> Date: Wed Sep 19 03:23:11 2012 -0500 Fix JRUBY-6885 method_missing and private methods behave differently public_send was unconditionally raising an error if the requested name was defined, but not public. Modified to redispatch to method_missing. :000000 100644 0000000... 48866d0... A spec/regression/JRUBY-6885_public_send_calls_method_missing_for_privates.rb :100644 100644 dc354fd... a3f36d4... M src/org/jruby/RubyKernel.java :100644 100644 ad2154a... 643d7d1... M src/org/jruby/RubyModule.java
        Hide
        nick added a comment -

        I just downloaded jruby-complete-1.7.0.RC1.jar and verified that my test case passes now. Thank you very much for your work folks!

        regards,

        nick.

        Show
        nick added a comment - I just downloaded jruby-complete-1.7.0.RC1.jar and verified that my test case passes now. Thank you very much for your work folks! regards, nick.

          People

          • Assignee:
            Charles Oliver Nutter
            Reporter:
            nick
          • Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: