Project

General

Profile

Actions

Bug #10969

closed

public_send in combination with method_missing raises NameError instead of NoMethodError

Added by senny (Yves Senn) over 9 years ago. Updated almost 9 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14]
[ruby-core:<unknown>]

Description

While working on the Rails project, specifically this issue https://github.com/rails/rails/issues/19297 I discovered that public_send can raise a NameError instead of a NoMethodError.

Following is a minimal reproduction scenario to trigger the bug. A more detailed example can be found in this Gist: https://gist.github.com/senny/9864a138defa322ed807

class Person
  def implicit_assignment
    nope rescue nil
    public_send "nope="
  end

  def method_missing(*args)
    super
  end
end

a = Person.new
a.implicit_assignment
# test.rb:13:in `method_missing': undefined local variable or method `nope=' for #<Person:0x007f91d1052ef8> (NameError)
# 	from test.rb:4:in `public_send'
# 	from test.rb:4:in `implicit_assignment'
# 	from test.rb:24:in `<main>'

What a found out during debugging:

I am not a C programmer and have very little experience in that field. While debugging the issue I could make some observations what's going on.

The error is being raised in raise_method_missing (https://github.com/ruby/ruby/blob/7790f37efdd8dd42a0a43c3206f6afdd43f8e86a/vm_eval.c#L704-L706):

else if (last_call_status & NOEX_VCALL) {
  format = "undefined local variable or method `%s' for %s";
  exc = rb_eNameError;

last_call_status is stored on the current thread (https://github.com/ruby/ruby/blob/7790f37efdd8dd42a0a43c3206f6afdd43f8e86a/vm_eval.c#L655):

The thread struct is modified in vm_call_method_missing (https://github.com/ruby/ruby/blob/7790f37efdd8dd42a0a43c3206f6afdd43f8e86a/vm_insnhelper.c#L1668):

th->method_missing_reason = ci->aux.missing_reason;

Now the problem is, that the call to public_send with the method name containing an = sign, does not modify the thread struct. This means that it still contains the value assigned from the previous call. That's what nope rescue nil in the reproduction is used for. It assigns NOEX_VCALL to that struct.

Actions #1

Updated by nobu (Nobuyoshi Nakada) over 9 years ago

  • Status changed from Open to Closed

Applied in changeset r51427.


vm_eval.c: set method_missing_reason

  • vm_eval.c (send_internal): set method_missing_reason before
    invoking overriding method_missing method so that the default
    method_missing can achieve it properly.
    [ruby-core:68515] [Bug #10969]

Updated by nobu (Nobuyoshi Nakada) over 9 years ago

  • Description updated (diff)
  • Backport set to 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: REQUIRED

Updated by senny (Yves Senn) over 9 years ago

Thank you Nobuyoshi Nakada and Koichi Sasada! 💛

Updated by nagachika (Tomoyuki Chikanaga) almost 9 years ago

  • Backport changed from 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: REQUIRED to 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: DONE

Backported into ruby_2_2 branch at r52771.
The MISSING_NOENTRY was introduced at r50743. I replaced MISSING_NOENTRY it to 0 to backport.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0