Any way to get the calling method's binding?

Something I've wanted to do on a few occasions recently is to evaluate
an expression in the context of the calling method. A simple example
is this:

  def trace(expr, _binding)
    LOG.debug "#{expr} = #{eval expr, _binding)}"
  end

  def something
    x = 5
    y = 7
    trace "x + y", binding
  end

The effect of calling something() now is that the message "x + y = 12"
gets logged.

It Would Be Nice if I could do this instead:

  def trace(expr)
    _binding = somehow_get_calling_methods_binding()
    LOG.debug "#{expr} = #{eval expr, _binding)}"
  end

  def something
    x = 5
    y = 7
    trace "x + y"
  end

Is there a substitute for "somehow_get_calling_methods_binding()" in
the code above?

Thanks,
Gavin

Gavin Sinclair wrote:

Something I've wanted to do on a few occasions recently is to evaluate
an expression in the context of the calling method. A simple example
is this:

It's being discussed:

http://rcrchive.net/rcr/RCR/RCR253

Gavin Sinclair wrote:

Something I've wanted to do on a few occasions recently is to evaluate
an expression in the context of the calling method.

Use this:

begin
  require 'simplecc'
rescue LoadError
  def Continuation.create(*args, &block)
    cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
    result ||= args
    return *[cc, *result]
  end
end

# This method returns the binding of the method that called your
# method. Don't use it when you're not inside a method.
#
# It's used like this:
# def inc_counter
# Binding.of_caller do |binding|
# eval("counter += 1", binding)
# end
# end
# counter = 0
# 2.times { inc_counter }
# counter # => 2
#
# You will have to put the whole rest of your method into the
# block that you pass into this method. If you don't do this
# an Exception will be raised. Because of the way that this is
# implemented it has to be done this way.
def Binding.of_caller(&block)
  old_critical = Thread.critical
  Thread.critical = true
  count = 0
  cc, result, error = Continuation.create(nil, nil)
  error.call if error

  tracer = lambda do |*args|
    type, context = args[0], args[4]
    if type == "return"
      count += 1
      # First this method and then calling one will return --
      # the trace event of the second event gets the context
      # of the method which called the method that called this
      # method.
      if count == 2
        # It would be nice if we could restore the trace_func
        # that was set before we swapped in our own one, but
        # this is impossible without overloading set_trace_func
        # in current Ruby.
        set_trace_func(nil)
        cc.call(eval("binding", context), nil)
      end
    elsif type != "line"
      set_trace_func(nil)
      error_msg = "Binding.of_caller used in non-method context or " +
        "trailing statements of method using it aren't in the block."
      cc.call(nil, lambda { raise(ArgumentError, error_msg ) })
    end
  end

  unless result
    set_trace_func(tracer)
    return nil
  else
    Thread.critical = old_critical
    yield result
  end
end

Regards,
Florian Gross

In the meantime, you could do this:

def trace (expr)
    LOG.debug "#{expr} = #{yield expr}"
end

def something
    x=5
    x=7
    trace "x+y", {|n| eval n}
end

something => 12

I'll admit that it'd be nice if the block passing could be automated; not sure how to do that off the top of my head. This reminds me of dynamic scoping; anybody got any clue about how to do away with the block? Maybe there's a way to do this with an extension?

Austin

Joel VanderWerf wrote:

···

Gavin Sinclair wrote:

Something I've wanted to do on a few occasions recently is to evaluate
an expression in the context of the calling method. A simple example
is this:

It's being discussed:

http://rcrchive.net/rcr/RCR/RCR253

"Florian Gross" <flgr@ccan.de> schrieb im Newsbeitrag
news:2j0eafFrmjefU1@uni-berlin.de...

Gavin Sinclair wrote:

> Something I've wanted to do on a few occasions recently is to evaluate
> an expression in the context of the calling method.

Use this:

> begin
> require 'simplecc'
> rescue LoadError
> def Continuation.create(*args, &block)
> cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and

args.empty?}

> result ||= args
> return *[cc, *result]
> end
> end
>
> # This method returns the binding of the method that called your
> # method. Don't use it when you're not inside a method.
> #
> # It's used like this:
> # def inc_counter
> # Binding.of_caller do |binding|
> # eval("counter += 1", binding)
> # end
> # end
> # counter = 0
> # 2.times { inc_counter }
> # counter # => 2
> #
> # You will have to put the whole rest of your method into the
> # block that you pass into this method. If you don't do this
> # an Exception will be raised. Because of the way that this is
> # implemented it has to be done this way.
> def Binding.of_caller(&block)
> old_critical = Thread.critical
> Thread.critical = true
> count = 0
> cc, result, error = Continuation.create(nil, nil)
> error.call if error
>
> tracer = lambda do |*args|

You're recreating the function on every invocation. Could be a performance
bottleneck.

> type, context = args[0], args[4]
> if type == "return"
> count += 1
> # First this method and then calling one will return --
> # the trace event of the second event gets the context
> # of the method which called the method that called this
> # method.
> if count == 2
> # It would be nice if we could restore the trace_func
> # that was set before we swapped in our own one, but
> # this is impossible without overloading set_trace_func
> # in current Ruby.
> set_trace_func(nil)
> cc.call(eval("binding", context), nil)
> end
> elsif type != "line"
> set_trace_func(nil)
> error_msg = "Binding.of_caller used in non-method context or " +
> "trailing statements of method using it aren't in the block."
> cc.call(nil, lambda { raise(ArgumentError, error_msg ) })
> end
> end
>
> unless result
> set_trace_func(tracer)
> return nil
> else
> Thread.critical = old_critical
> yield result
> end
> end

Regards,
Florian Gross

This seems much simpler to me...

set_trace_func lambda { |event, file, line, id, binding, classname|
  case event
    when "call"
      ( Thread.current[:bindings] ||= ).push binding
    when "return"
      Thread.current[:bindings].pop
  end
}

module Kernel
  private

  def caller_binding
    Thread.current[:bindings][-3]
  end
end

# demo usage:

def test2
  # set x for demonstration purposes
  x = 99
  p eval( "x", caller_binding )
end

def test
  x = 10
  test2
  x = 20
  test2
end

test

Regards

    robert

This is a very creative piece of work, congratulations !
I am adding that to my rcr.rb with the proper credit.

I guess that it is not extremely efficient but it will
help me a lot in my exception logging scheme. So far I
used to explicitly pass the binding to be able to dump
the local variables when required. Thanks !

For more speed demanding usage, there would be some need
for native support I guess, but as of today your
solution is probably very close to the most efficient
solution.

BTW: What is "simplecc.rb" ?

Yours,

JeanHuguesRobert

···

At 22:23 12/06/2004 +0900, you wrote:

Gavin Sinclair wrote:

Something I've wanted to do on a few occasions recently is to evaluate
an expression in the context of the calling method.

Use this:

begin
require 'simplecc'
rescue LoadError
def Continuation.create(*args, &block)
   cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
   result ||= args
   return *[cc, *result]
end
end
# This method returns the binding of the method that called your
# method. Don't use it when you're not inside a method.
#
# It's used like this:
# def inc_counter
# Binding.of_caller do |binding|
# eval("counter += 1", binding)
# end
# end
# counter = 0
# 2.times { inc_counter }
# counter # => 2
#
# You will have to put the whole rest of your method into the
# block that you pass into this method. If you don't do this
# an Exception will be raised. Because of the way that this is
# implemented it has to be done this way.
def Binding.of_caller(&block)
old_critical = Thread.critical
Thread.critical = true
count = 0
cc, result, error = Continuation.create(nil, nil)
error.call if error
tracer = lambda do |*args|
   type, context = args[0], args[4]
   if type == "return"
     count += 1
     # First this method and then calling one will return --
     # the trace event of the second event gets the context
     # of the method which called the method that called this
     # method.
     if count == 2
       # It would be nice if we could restore the trace_func
       # that was set before we swapped in our own one, but
       # this is impossible without overloading set_trace_func
       # in current Ruby.
       set_trace_func(nil)
       cc.call(eval("binding", context), nil)
     end
   elsif type != "line"
     set_trace_func(nil)
     error_msg = "Binding.of_caller used in non-method context or " +
       "trailing statements of method using it aren't in the block."
     cc.call(nil, lambda { raise(ArgumentError, error_msg ) })
   end
end
unless result
   set_trace_func(tracer)
   return nil
else
   Thread.critical = old_critical
   yield result
end
end

Regards,
Florian Gross

-------------------------------------------------------------------------
Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

Austin McDonald wrote:

In the meantime, you could do this:

def trace (expr)
   LOG.debug "#{expr} = #{yield expr}"
end

def something
   x=5
   x=7
   trace "x+y", {|n| eval n}
end

something => 12

def trace(&block)
   expr = block.call
   puts "#{expr} = #{eval(expr, block)}"
end

def something
   x = 5
   y = 7
   trace {"x + y"}
end

something # ==> x + y = 12

Robert Klemme wrote:

This seems much simpler to me...

set_trace_func lambda { |event, file, line, id, binding, classname|
  case event
    when "call"
      ( Thread.current[:bindings] ||= ).push binding
    when "return"
      Thread.current[:bindings].pop
  end
}

Global trace_funcs are a *huge* performance bottle neck. That's why I'm doing so much work to make sure that the trace_func will go away as soon as possible.

Regards,
Florian Gross

Jean-Hugues ROBERT wrote:

[Binding.of_caller]

This is a very creative piece of work, congratulations !
I am adding that to my rcr.rb with the proper credit.

Thanks. :slight_smile:

I guess that it is not extremely efficient but it will
help me a lot in my exception logging scheme. So far I
used to explicitly pass the binding to be able to dump
the local variables when required. Thanks !

Nice to hear that it's useful. I actually need it for implementing a wrapper around eval(). (I need to give unique file descriptors to evaluated code in proc_source.rb so that I can later map them back to code.)

For more speed demanding usage, there would be some need
for native support I guess, but as of today your
solution is probably very close to the most efficient
solution.

I dunno how fast it is -- we would need to do some benchmarking to find that out -- but at least it is only slow where it's being used. :slight_smile:

BTW: What is "simplecc.rb" ?

It's the same code that is included in the rescue part. It's the only way I can use Continuations without going insane.

Yours,
JeanHuguesRobert

Regards,
Florian Gross

Excellent; I had the right idea, but yours is much cleaner.

For giggles, and maybe b/c it's a little more explicit, you could use Kernel#binding to make it obvious what's going on:

def trace(expr, b)
    LOG.debug "#{expr} = #{eval(expr, b)}"
end

def something()
    x=5
    y=7
    trace "x+y", binding
end

something # ==> x+y = 12

Although I like Joel's version better, I think this is a little clearer.

Austin

Joel VanderWerf wrote:

···

Austin McDonald wrote:

In the meantime, you could do this:

def trace (expr)
   LOG.debug "#{expr} = #{yield expr}"
end

def something
   x=5
   x=7
   trace "x+y", {|n| eval n}
end

something => 12

def trace(&block)
  expr = block.call
  puts "#{expr} = #{eval(expr, block)}"
end

def something
  x = 5
  y = 7
  trace {"x + y"}
end

something # ==> x + y = 12

"Florian Gross" <flgr@ccan.de> schrieb im Newsbeitrag
news:2j0ktoFrkk6bU1@uni-berlin.de...

Robert Klemme wrote:

> This seems much simpler to me...
>
> set_trace_func lambda { |event, file, line, id, binding, classname|
> case event
> when "call"
> ( Thread.current[:bindings] ||= ).push binding
> when "return"
> Thread.current[:bindings].pop
> end
> }

Global trace_funcs are a *huge* performance bottle neck. That's why I'm
doing so much work to make sure that the trace_func will go away as soon
as possible.

Ah, ok. I thought there should be some advantage...

    robert

Umm that's exactly what gavin was trying to avoid, explicitely passing the
binding...

Charles Comstock

···

On Sat, 12 Jun 2004, Austin McDonald wrote:

Excellent; I had the right idea, but yours is much cleaner.

For giggles, and maybe b/c it's a little more explicit, you could use
Kernel#binding to make it obvious what's going on:

def trace(expr, b)
    LOG.debug "#{expr} = #{eval(expr, b)}"
end

def something()
    x=5
    y=7
    trace "x+y", binding
end

something # ==> x+y = 12

Although I like Joel's version better, I think this is a little clearer.

Austin

Right. I'm out of it at the moment; got wrapped up in following the idea trails and wound up where Gavin started... my apologies. I think I'll get some sleep.

Austin

Austin McDonald wrote:

···

Excellent; I had the right idea, but yours is much cleaner.

For giggles, and maybe b/c it's a little more explicit, you could use Kernel#binding to make it obvious what's going on:

def trace(expr, b)
   LOG.debug "#{expr} = #{eval(expr, b)}"
end

def something()
   x=5
   y=7
   trace "x+y", binding
end

something # ==> x+y = 12

Although I like Joel's version better, I think this is a little clearer.

Austin

Joel VanderWerf wrote:

Austin McDonald wrote:

In the meantime, you could do this:

def trace (expr)
   LOG.debug "#{expr} = #{yield expr}"
end

def something
   x=5
   x=7
   trace "x+y", {|n| eval n}
end

something => 12

def trace(&block)
  expr = block.call
  puts "#{expr} = #{eval(expr, block)}"
end

def something
  x = 5
  y = 7
  trace {"x + y"}
end

something # ==> x + y = 12