How I can create/extract a variable/hash into the current binding in Ruby?

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,

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.

Ramon de C Valle wrote in post #1088790:

Hi,

the following results in a NameError:

hash = {:a => 1}

puts a

Yep.

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,

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.

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 (

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 (

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
pointerhttp://lifegoo.pluskid.org/upload/doc/yarv/yarv_iset.html#detail-1
(“environment
pointer” as of June
2012http://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. :slight_smile:

— Arlen

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,

Hi,

On 12/12/2012 08:57 AM, Arlen Christian Mart C. 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. :slight_smile:
Thank you for the additional information! Any suggestions of a possible
“more Ruby” way of doing this?

On Wed, Dec 12, 2012 at 12:40 PM, Ramon de C Valle [email protected]
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

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

Hi,

On 12/12/2012 11:29 AM, Robert K. wrote:

On Wed, Dec 12, 2012 at 12:40 PM, Ramon de C Valle [email protected] 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/master/bin/active_file.rb#L44).

If accesses


remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Thanks,

On Wed, Dec 12, 2012 at 2:44 PM, Ramon de C Valle [email protected]
wrote:

On 12/12/2012 11:29 AM, Robert K. wrote:

On Wed, Dec 12, 2012 at 12:40 PM, Ramon de C Valle [email protected] 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/master/bin/active_file.rb#L44).

I have no idea what you are doing there but the huge amount of
repeated code looks highly suspicious.

Cheers

robert