Project

General

Profile

Bug #16251

Evaluation in binding differs from ruby execution

Added by teslur (Tetsushi FUKABORI) 12 months ago. Updated 11 months ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-darwin18]
[ruby-core:95332]

Description

In specific situation, I found that result of string evaluation in Binding returns different from ruby execution result.

In following sample code, ruby evaluates method_or_local_var as method call and returns "method".
However, binding.eval evaluates method_or_local_var as local variable and returns nil.

Here is sample code.

def method_or_local_var
  'method'
end

if true
  puts "execute method_or_local_var:"
  p method_or_local_var #=> "method"

  puts "execute method_or_local_var by bind.eval('method_or_local_var'):"
  p binding.eval('method_or_local_var') #=> nil
else
  method_or_local_var = 'local variable'
end

and here is results of execute sample code.

❯ ruby sample_code.rb
execute method_or_local_var:
"method"
execute method_or_local_var by bind.eval('method_or_local_var'):
nil

I expect evaluation result of method_or_local_var in binding to be method, and returns "method".
Is this the expected behavior?

Updated by mame (Yusuke Endoh) 12 months ago

I have no idea whether this is a spec or undefined behavior, but the current implementation is actually intentional.

def x
  "x"
end

def foo
  eval("x = 1") # foo has no variable named x, so 1 is assigned to a temporal variable
  p eval("x") #=> "x" # there is no variable named x in this scope, so it is regarded as a method call
end
foo

def bar
  eval("x = 1") # foo has a variable named x, so 1 is assigned to the variable
  p eval("x") #=> 1 # foo has a variable named x, so it is regarded as a variable reference
  x = nil
end
bar

IMO, you'd better not to depend upon the behavior in any way.

Updated by Eregon (Benoit Daloze) 12 months ago

Local variables are "hoisted" to the beginning of the method/block in Ruby (and start with value nil).
With that in mind, I think the behavior is logical. IMHO, it is spec.

#3

Updated by jeremyevans0 (Jeremy Evans) 12 months ago

  • Status changed from Open to Closed

Updated by kernigh (George Koehler) 11 months ago

This is a simpler example of the behavior:

$ ruby -e 'p x; x = 6'
Traceback (most recent call last):
-e:1:in `<main>': undefined local variable or method `x' for main:Object (NameError)
$ ruby -e 'p eval("x"); x = 6'
nil
$ ruby -e 'p binding.eval("x"); x = 6'
nil

A plain x raises NameError, but an eval("x") or binding.eval("x") fetches nil from the local variable x. I expect this behavior, because Ruby parses a script before running it:

  1. Ruby parses x = 6 and adds x to the binding.
  2. Ruby runs eval("x") with x in the binding.
  3. Ruby runs x = 6 and changes the value of x from nil to 6.

Binding#local_variables reveals the local variable named x:

$ ruby -e 'p binding.local_variables; x = 6'
[:x]

Because x exists, binding.eval("x") has the expected behavior, so there is no bug.

Also available in: Atom PDF