Project

General

Profile

Actions

Bug #21125

open

Kernel is called first

Added by mikik0 (Hashino Mikiko) 13 days ago. Updated 13 days ago.

Status:
Open
Target version:
-
ruby -v:
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
[ruby-core:120915]

Description

Kernel#system is being called when there is a column or method name named system.
The other methods of Kernel reproduce the problem.
Ex.Rand returns a random value, and exit terminates the program.

This problem did not occur until Ruby 3.3 series, but has occurred since Ruby 3.4.

Reproducible script:

class ObjectifiedHash
  def initialize(hash)
    @hash = hash
  end

  private

  attr_reader :hash

  def method_missing(method_name, *args, &block)
    if hash.key?(method_name.to_sym)
      hash[method_name.to_sym]
    else
      super
    end
  end

  def respond_to_missing?(method_name, include_private = false)
    hash.key?(method_name.to_sym) || super
  end
end

class Foo
  def system(...)
    # Kernel#system is called
    raw.system(...)
    # (...) passes if there is no
    # raw.system
  end

  def initialize(raw)
    @raw = raw
  end

  def get_system
    system
  end

  private

  attr_reader :raw
end

class Test
  def self.run
    obj = ObjectifiedHash.new({
      system: 'system'
    })

    foo = Foo.new(obj)
    # foo.system would pass.
    # After executing foo.system, hoge.get_system also passes.
    # p foo.system
    p foo.get_system
  end
end

Test.run

Execution Results

hoge.rb:26:in 'Kernel#system': wrong number of arguments (given 0, expected 1+) (ArgumentError)

    raw.system(...)
               ^^^
        from ruby.rb:26:in 'Foo#system'
        from ruby.rb:36:in 'Foo#get_system'
        from ruby.rb:54:in 'Test.run'
        from ruby.rb:58:in '<main>'

Expected results

"system"

PR that may be relevant: https://github.com/ruby/ruby/pull/11028

Updated by jeremyevans0 (Jeremy Evans) 13 days ago

  • Status changed from Open to Rejected

This isn't a bug. If you want method_missing to be called instead of a method in Kernel, you need to undefine the existing method:

class ObjectifiedHash
  undef_method :system

  # rest of your example

You could consider the following instead, so that methods in Kernel are not included in the class:

class ObjectifiedHash < BasicObject
  # rest of your example

Updated by mikik0 (Hashino Mikiko) 13 days ago

@jeremyevans0 (Jeremy Evans) san
It works with Ruby 3.3 series, but not from Ruby 3.4. Is it a specification?

Updated by jeremyevans0 (Jeremy Evans) 13 days ago

  • Status changed from Rejected to Open

mikik0 (Hashino Mikiko) wrote in #note-2:

@jeremyevans0 (Jeremy Evans) san
It works with Ruby 3.3 series, but not from Ruby 3.4. Is it a specification?

You are correct. It also works on Ruby 3.4 when using --parser=parse.y, so this appears to be related to prism. I'll have to do more analysis to determine which behavior is correct, but it's certainly a bug that the behavior is not the same between prism and parse.y.

Updated by jeremyevans0 (Jeremy Evans) 13 days ago

jeremyevans0 (Jeremy Evans) wrote in #note-3:

mikik0 (Hashino Mikiko) wrote in #note-2:

@jeremyevans0 (Jeremy Evans) san
It works with Ruby 3.3 series, but not from Ruby 3.4. Is it a specification?

You are correct. It also works on Ruby 3.4 when using --parser=parse.y, so this appears to be related to prism. I'll have to do more analysis to determine which behavior is correct, but it's certainly a bug that the behavior is not the same between prism and parse.y.

Sorry, that was me testing the wrong version. The behavior is the same between prism and parse.y, so this is a difference in behavior between Ruby 3.3 and 3.4. I still need to more analysis to determine which behavior is correct.

Updated by jeremyevans0 (Jeremy Evans) 13 days ago

  • Assignee set to tenderlovemaking (Aaron Patterson)

The reason this works in Ruby 3.3 and below is that system is a private method and not a public method, and calls to private methods where the receiver is not self call method_missing. raw.system is a method call where the receiver is not self, but Kernel#system is a private method, so this should be calling method_missing. The fact that method_missing is not called in Ruby 3.4 is a bug. I've determined the bug is in generic argument forwarding. If you switch ... (generic argument forwarding) to *, **, & (anonymous splat usage), things work correctly. Assigning to @tenderlovemaking (Aaron Patterson).

Actions #6

Updated by byroot (Jean Boussier) 13 days ago

  • Backport changed from 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN to 3.1: DONTNEED, 3.2: DONTNEED, 3.3: DONTNEED, 3.4: REQUIRED
Actions #7

Updated by mikik0 (Hashino Mikiko) 13 days ago

  • Description updated (diff)
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0