Bug #13774
closed
for methods defined from procs, the binding of the resulting bound_method.to_proc does not have access to the original proc's closure environment
Added by bughit (bug hit) over 6 years ago.
Updated over 6 years ago.
Description
def foo
o = Object.new
local_var = 'local_var'
method_lambda = ->{local_var}
o.define_singleton_method(:lambda_method, &method_lambda)
method_proc = o.method(:lambda_method).to_proc
puts method_proc.()
puts eval('local_var', method_lambda.binding)
puts o.lambda_method
puts eval('local_var', method_proc.binding) # undefined local variable or method `local_var'
end
foo
when executed as a method it has access to local_var as expected
but the binding of the bound method proc does not
in ruby 2.2.2 the above code crashes the vm
bughit (bug hit) wrote:
def foo
o = Object.new
local_var = 'local_var'
method_lambda = ->{local_var}
o.define_singleton_method(:lambda_method, &method_lambda)
method_proc = o.method(:lambda_method).to_proc
puts method_proc.()
puts eval('local_var', method_lambda.binding)
puts o.lambda_method
puts eval('local_var', method_proc.binding) # undefined local variable or method `local_var'
end
foo
when executed as a method it has access to local_var as expected
but the binding of the bound method proc does not
in ruby 2.2.2 the above code crashes the vm
should also underscore that the bound method proc has access to local_var: method_proc.()
but its binding does not eval('local_var', method_proc.binding)
, so there's a clear discrepancy between a proc and its binding
- Subject changed from for methods defined from procs, the binding of the resulting bound method proc does not have access to the original proc's closure environment to for methods defined from procs, the binding of the resulting bound_method.to_proc does not have access to the original proc's closure environment
- ruby -v changed from ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux] to ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
def foo
o = Object.new
local_var = 'has access to captured variable'
original_proc = ->{local_var}
o.define_singleton_method(:method_from_proc, &original_proc)
method_proc = o.method(:method_from_proc).to_proc
puts "original_proc.call #{original_proc.()}"
puts "original_proc.binding #{original_proc.binding.eval('local_var')}"
puts "#method_from_proc #{o.method_from_proc}"
puts "method(:method_from_proc).to_proc.call #{method_proc.()}"
puts "method(:method_from_proc).to_proc.binding #{method_proc.binding.eval('local_var') rescue "does not have access to captured variable: #{$!.inspect}"}"
end
foo
method(:method_from_proc).to_proc.binding does not have access to captured variable: #<NameError: undefined local variable or method
local_var'`
This is a bug?
I think it is not a bug.
Local variables are not set up in the context of a binding from a method.
$ ruby -e 'def m(v=12);v;end; p method(:m).to_proc.binding.eval("v")'
Traceback (most recent call last):
2: from -e:1:in `<main>'
1: from -e:1:in `eval'
-e:1:in `<empty iseq>': undefined local variable or method `v' for main:Object (NameError)
nobu (Nobuyoshi Nakada) wrote:
I think it is not a bug.
Local variables are not set up in the context of a binding from a method.
$ ruby -e 'def m(v=12);v;end; p method(:m).to_proc.binding.eval("v")'
Traceback (most recent call last):
2: from -e:1:in `<main>'
1: from -e:1:in `eval'
-e:1:in `<empty iseq>': undefined local variable or method `v' for main:Object (NameError)
You are accessing a local variable in the method (which does not exist until the method is invoked), whereas I am accessing a variable outside the method, captured by the proc that defined the method, which does exist outside the invocation of the method. So your example does not explain the behavior that I am highlighting.
To try answer my own question, I can see how this might not be a bug if method.to_proc
returns a proc that is no way related to the proc that defined the method. Such a proc simply forwards the args to the method and knows nothing about the original proc binding. I initially thought that it would somehow inherit the binding of the original proc.
Once a Proc
is bound as a method, the context of that method is not same as the based Proc
, receiver
for instance.
x = ->{}
o = Object.new
o.define_singleton_method(:x, x)
p x.binding.receiver #=> main
p o.method(:x).to_proc.binding.receiver #=> o
- Status changed from Open to Rejected
Also available in: Atom
PDF
Like0
Like0Like0Like0Like0Like0Like0