Feature #1906

Kernel#backtrace: Objectifying Kernel#caller

Added by Run Paint Run Run over 4 years ago. Updated over 1 year ago.

[ruby-core:24805]
Status:Closed
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:core
Target version:2.0.0

Description

=begin
Inspired by nobu's recent refactoring of Kernel#rand`, several conversations with Ruby implementors about Kernel#caller, and Rubinius' Backtrace class, I've put together a rough demo of how we could "objectify" Kernel#caller. It's at http://tinyurl.com/m9fdrn [github.com], along with some initial specs.

Rationale
=========

caller has two principle uses:

  • Allowing users to display the backtrace at a given point, e.g. puts caller.
  • Introspection to determine the callpath that lead the current method.

    The first use is reasonably achievable with caller, as long as you don't want to do any formatting of the output. The second is hard because it requires parsing lines of the caller Array with regular expressions, and knowing what the various permutations of output imply. It would be easier if we could inspect the call stack with a Ruby-ish API. Further, this would allow alternative implementations to provide this functionality without having to reverse-engineer the output of caller. As a result, backtraces would become more useful and code using them more portable.

    Name

    The advantage of calling this feature #backtrace is that it's consistent with the usage of the term by Thread and Exception. This, however, could also be construed as a disadvantage because although identically named the output would be materially different. I'm not sure of the best approach in this regard.

    API

    A Kernel method named, for sake of argument, 'backtrace' which returns a Backtrace object. It can be treated like an Array, in the same way caller is, because it's an Enumerable. It also has shortcuts for accessing the most recent entry on the stack. Each line in the backtrace is represented by a Backtrace::Line object which has #file, #line, and #name accessors which correspond to the filename, the line number, and the method name, respectively. For example:

    backtrace.name # The name of the method which invoked the current one as a Symbol
    backtrace.file(2) # The absolute filename of the 3rd entry in the backtrace
    backtrace.each do |line| # Yields Line objects
    puts line.method
    end
    backtrace.lines.select {|l| l.method == :foo} # #lines returns an Array of Line objects

    Simple stuff.

    Weaknesses

    Ideally, #name (not called #method because of the clash with Object#method) would return a Method object. One of the many advantages of this would be that we could combine backtraces with Method#parameters to display the signatures of each method. Unfortunately, I can't see a non-hackish way to create Method objects from the output of caller, because I don't know which object the method is bound to. but if this were possible it would be useful.

    I'm currently throwing away some of the output of caller because I don't completely understand it. We'll need to decide whether this would be useful to expose via the API, and if so how.

    I'd prefer to return a File object for #file, but the majority of Ruby APIs return pathnames instead, so I've went with convention.

    So is there any interest in this type of thing? Is it worth exploring further?
    =end


Related issues

Related to ruby-trunk - Feature #839: Add code on each line of a backtrace output to the screen Rejected 12/08/2008
Related to ruby-trunk - Feature #3917: [proposal] called_from() which is much faster than caller() Closed 10/08/2010
Related to ruby-trunk - Feature #5016: Kernel#caller with negative limit should limit result to ... Closed 07/11/2011

History

#1 Updated by Yehuda Katz over 4 years ago

=begin
Ha. I mentioned that this would be desirable in a different thread. Put
me down for an enthusiastic +1.

Have you looked at Rubinius' objectified backtrace?

-- Yehuda

Run Paint Run Run wrote:

Feature #1906: Kernel#backtrace: Objectifying Kernel#caller
http://redmine.ruby-lang.org/issues/show/1906

Author: Run Paint Run Run
Status: Open, Priority: Normal
Category: core

Inspired by nobu's recent refactoring of Kernel#rand`, several conversations with Ruby implementors about Kernel#caller, and Rubinius' Backtrace class, I've put together a rough demo of how we could "objectify" Kernel#caller. It's at http://tinyurl.com/m9fdrn [github.com], along with some initial specs.

Rationale

caller has two principle uses:

  • Allowing users to display the backtrace at a given point, e.g. puts caller.
  • Introspection to determine the callpath that lead the current method.

The first use is reasonably achievable with caller, as long as you don't want to do any formatting of the output. The second is hard because it requires parsing lines of the caller Array with regular expressions, and knowing what the various permutations of output imply. It would be easier if we could inspect the call stack with a Ruby-ish API. Further, this would allow alternative implementations to provide this functionality without having to reverse-engineer the output of caller. As a result, backtraces would become more useful and code using them more portable.

Name

The advantage of calling this feature #backtrace is that it's consistent with the usage of the term by Thread and Exception. This, however, could also be construed as a disadvantage because although identically named the output would be materially different. I'm not sure of the best approach in this regard.

API

A Kernel method named, for sake of argument, 'backtrace' which returns a Backtrace object. It can be treated like an Array, in the same way caller is, because it's an Enumerable. It also has shortcuts for accessing the most recent entry on the stack. Each line in the backtrace is represented by a Backtrace::Line object which has #file, #line, and #name accessors which correspond to the filename, the line number, and the method name, respectively. For example:

 backtrace.name # The name of the method which invoked the current one as a Symbol
 backtrace.file(2) # The absolute filename of the 3rd entry in the backtrace
 backtrace.each do |line| # Yields Line objects
   puts line.method
 end
 backtrace.lines.select {|l| l.method == :foo} # #lines returns an Array of Line objects

Simple stuff.

Weaknesses

Ideally, #name (not called #method because of the clash with Object#method) would return a Method object. One of the many advantages of this would be that we could combine backtraces with Method#parameters to display the signatures of each method. Unfortunately, I can't see a non-hackish way to create Method objects from the output of caller, because I don't know which object the method is bound to. but if this were possible it would be useful.

I'm currently throwing away some of the output of caller because I don't completely understand it. We'll need to decide whether this would be useful to expose via the API, and if so how.

I'd prefer to return a File object for #file, but the majority of Ruby APIs return pathnames instead, so I've went with convention.

So is there any interest in this type of thing? Is it worth exploring further?


http://redmine.ruby-lang.org

=end

#2 Updated by Kazuhiro NISHIYAMA about 4 years ago

  • Target version set to 2.0.0

=begin

=end

#3 Updated by Yui NARUSE almost 3 years ago

  • Status changed from Open to Assigned
  • Assignee set to Yukihiro Matsumoto

#4 Updated by Denis de Bernardy almost 3 years ago

Is this (or #3917, which also looks neat) anything that might make it into ruby 1.9.3? I was wondering how to get the calling file's name earlier today without resorting to caller() -- which yields an unnecessarily large string array.

As an aside, there's this sender gem written in C here, in the meanwhile, which implements something very similar to the suggested backtrace() function:

https://github.com/Asher-/sender

#5 Updated by Koichi Sasada over 1 year ago

  • Status changed from Assigned to Closed

I close this issue because #3917 was accepted.
Please re-open I'm misunderstanding.

Also available in: Atom PDF