Hi,
How I can create/extract a variable/hash into the current binding in
Ruby? For instance, the following results in a NameError:
class Hash
def extract(b)
self.each do |key, value|
bind = b.eval <<-END
#{key} = nil
proc { |value| #{key} = value }
END
bind.call(value)
end
end
end
hash = {:a => 1}
hash.extract(binding)
puts a
Noteworthy mentioning, each call to Kernel#binding returns a different
Binding object instance, which makes me think that I'm not really
changing the binding of the caller of Hash#extract. For instance, the
following works:
class Hash
def extract(b)
self.each do |key, value|
bind = b.eval <<-END
#{key} = nil
proc { |value| #{key} = value }
END
bind.call(value)
end
end
end
hash = {:a => 1}
b = binding
hash.extract(b)
eval('puts a', b)
Thanks,
on 2012-12-12 10:28
on 2012-12-12 11:23
Hi, I'm no expert on this stuff, but I think it's a problem of the Ruby interpreter determining variables while it still parses the code: eval 'x = 1' puts x This doesn't work, because the parser doesn't "see" the eval stuff. It doesn't find an assignment for "x", so it assumes it's a method (which leads to a NameError). If, however, you set "x" to an arbitrary value beforehands, it correctly regognizes "x" as a variable: x = nil eval 'x = 1' puts x Apart from the technical stuff: I don't find it a good idea to just dump variables into a binding, because it's just too much "magic". It makes it hard to keep track of which variable has which value. And I can't think of any good reason to do that when you're programming object-oriented. Did you get the idea from PHP? Because that language is notorious for dumping values into all kinds of contexts, which has lead and still leads to gigantic security problems.
on 2012-12-12 11:25
Ramon de C Valle wrote in post #1088790: > Hi, > > the following results in a NameError: > > hash = {:a => 1} > > puts a Yep.
on 2012-12-12 11:29
Hi, On 12/12/2012 08:23 AM, Jan E. wrote: > leads to a NameError). > it hard to keep track of which variable has which value. And I can't > think of any good reason to do that when you're programming > object-oriented. > > Did you get the idea from PHP? Because that language is notorious for > dumping values into all kinds of contexts, which has lead and still > leads to gigantic security problems. For my particular case, I just want to avoid a giant list of assignments for creating local variables within a method while retrieving information from a series of nested FFI Structs. > Thanks,
on 2012-12-12 11:38
As to the solution: If you want to keep this approach, I think you'll have no other choice than to also wrap the variable uses in an "eval" call. Because whenever the parser finds one of those dynamically created variables, it won't recognize it as a variable and throw a NameError.
on 2012-12-12 11:57
To expand on Jan's comments, note the difference in disassemblies: >> puts RubyVM::InstructionSequence.disasm lambda { eval 'x = 1'; puts x } == disasm: <RubyVM::InstructionSequence:block in irb_binding@(irb)>===== == catch table | catch type: redo st: 0000 ed: 0028 sp: 0000 cont: 0000 | catch type: next st: 0000 ed: 0028 sp: 0000 cont: 0028 |------------------------------------------------------------------------ 0000 trace 1 ( 3) 0002 putself 0003 putstring "x = 1" 0005 send :eval, 1, nil, 8, <ic:0> 0011 pop 0012 trace 1 0014 putself 0015 putself 0016 send :x, 0, nil, 24, <ic:1> 0022 send :puts, 1, nil, 8, <ic:2> 0028 leave => nil >> puts RubyVM::InstructionSequence.disasm lambda { x = nil; eval 'x = 1'; puts x } == disasm: <RubyVM::InstructionSequence:block in irb_binding@(irb)>===== == catch table | catch type: redo st: 0000 ed: 0030 sp: 0000 cont: 0000 | catch type: next st: 0000 ed: 0030 sp: 0000 cont: 0030 |------------------------------------------------------------------------ local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1) [ 2] x 0000 trace 1 ( 4) 0002 putnil 0003 setdynamic x, 0 0006 trace 1 0008 putself 0009 putstring "x = 1" 0011 send :eval, 1, nil, 8, <ic:0> 0017 pop 0018 trace 1 0020 putself 0021 getdynamic x, 0 0024 send :puts, 1, nil, 8, <ic:1> 0030 leave => nil In the first instance, the parser decides that, having seen no local x, it must be a function call, and so "0016 send :x, 0, nil, 24, <ic:1>" is emitted. In the second, having already seen an x ("0003 setdynamic x, 0"), it knows that it is referring to the local var, so it emits "0021 getdynamic x, 0" instead of a call. Unfortunately, there's no in between where it could refer to either a local or function, because the compiler needs to know all locally-referable variables in order to emit the instructions; "setdynamic x, 0" is actually "setdynamic 2, 0" (note the 'local table' before the instructions in the second disassembly, and the total lack of same in the first one), which is why local variables are so fast; they're just direct offsets from the local frame pointer<http://lifegoo.pluskid.org/upload/doc/yarv/yarv_is... ("environment pointer" as of June 2012<http://comments.gmane.org/gmane.comp.lang.ruby.cvs/39182> ). So, in short, VM constraints are responsible for this being impossible. You could either just say 'a = b = c = d = … = nil' before your hash.extract to make them findable, or find a possibly "more Ruby" way. :) — Arlen
on 2012-12-12 12:38
Hi, On 12/12/2012 08:38 AM, Jan E. wrote: > As to the solution: > > If you want to keep this approach, I think you'll have no other choice > than to also wrap the variable uses in an "eval" call. Because whenever > the parser finds one of those dynamically created variables, it won't > recognize it as a variable and throw a NameError. Actually, this is what I'm currently doing now. I extract the structures immediately after entering the method and wrap the rest into an eval. > Thanks,
on 2012-12-12 12:41
Hi, On 12/12/2012 08:57 AM, Arlen Christian Mart Cuss wrote: > 0003 putstring "x = 1" > 1'; puts x } > 0006 trace 1 > > locally-referable variables in order to emit the instructions; > hash.extract to make them findable, or find a possibly "more Ruby" way. :) Thank you for the additional information! Any suggestions of a possible "more Ruby" way of doing this?
on 2012-12-12 13:13
A simpler test case: def foo(b) b.eval "a = 1" end b = binding foo(b) puts a # NameError: undefined local variable or method 'a' b.eval "puts a" # this is fine puts local_variables # shows both a and b
on 2012-12-12 14:30
On Wed, Dec 12, 2012 at 12:40 PM, Ramon de C Valle <rcvalle@redhat.com> wrote: > Thank you for the additional information! Any suggestions of a possible > "more Ruby" way of doing this? Keep the data in the Hash and use it there. If you think about it you will need eval for manipulation of the data as well because you do not know beforehand what local variables you have, don't you? If accesses like hash[:a] are just inconvenient you could copy the Hash into an OpenStruct and do hash.a which might be slightly nicer. Generating local variables is almost always a bad idea. Kind regards robert
on 2012-12-12 14:44
Hi, On 12/12/2012 11:29 AM, Robert Klemme wrote: > On Wed, Dec 12, 2012 at 12:40 PM, Ramon de C Valle <rcvalle@redhat.com> wrote: > >> Thank you for the additional information! Any suggestions of a possible >> "more Ruby" way of doing this? > > Keep the data in the Hash and use it there. If you think about it you > will need eval for manipulation of the data as well because you do not > know beforehand what local variables you have, don't you? Yes, I know beforehand because they are predefined FFI structs. I'm just trying to avoid things like event[:u][:exception][:exception_record][:exception_information][0] (see https://github.com/rcvalle/active_fuzzer/blob/mast...). If accesses > -- > remember.guy do |as, often| as.you_can - without end > http://blog.rubybestpractices.com/ > Thanks,
on 2012-12-12 16:43
On Wed, Dec 12, 2012 at 2:44 PM, Ramon de C Valle <rcvalle@redhat.com> wrote: > On 12/12/2012 11:29 AM, Robert Klemme wrote: >> On Wed, Dec 12, 2012 at 12:40 PM, Ramon de C Valle <rcvalle@redhat.com> wrote: >> >>> Thank you for the additional information! Any suggestions of a possible >>> "more Ruby" way of doing this? >> >> Keep the data in the Hash and use it there. If you think about it you >> will need eval for manipulation of the data as well because you do not >> know beforehand what local variables you have, don't you? > Yes, I know beforehand because they are predefined FFI structs. Whatever that is. > I'm just > trying to avoid things like > event[:u][:exception][:exception_record][:exception_information][0] (see > https://github.com/rcvalle/active_fuzzer/blob/mast...). I have no idea what you are doing there but the huge amount of repeated code looks highly suspicious. Cheers robert
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.