Project

General

Profile

Actions

Bug #16251

closed

Evaluation in binding differs from ruby execution

Added by teslur (Tetsushi FUKABORI) over 4 years ago. Updated over 4 years ago.

Status:
Closed
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) over 4 years 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) over 4 years 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.

Actions #3

Updated by jeremyevans0 (Jeremy Evans) over 4 years ago

  • Status changed from Open to Closed

Updated by kernigh (George Koehler) over 4 years 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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0