Bug #17160
closedWrong exception backtrace
Description
Looking at web-console test results 1, there is following error reported:
WebConsole::EvaluatorTest#test_Evaluator_callers_are_cleaned_up_of_unneeded_backtraces [/home/travis/build/rails/web-console/test/web_console/evaluator_test.rb:63]:
--- expected
+++ actual
@@ -1,3 +1,3 @@
"RuntimeError: oops
-\tfrom /home/travis/build/rails/web-console/test/web_console/evaluator_test.rb:61:in `block in <class:EvaluatorTest>'
+\tfrom (eval):1:in `block in <class:EvaluatorTest>'
"
Trying to reproduce the issue, it seems that the exception backtrace is not correct. I have used slightly modified version of 2 included in attachment and the output is:
$ ruby -ractive_support -r./evaluator -e 'e = WebConsole::Evaluator.new(binding); puts e.eval("raise %{oops}")'
# exc.backtrace #
-e:1:in `<main>'
/builddir/t/evaluator.rb:22:in `eval'
/builddir/t/evaluator.rb:22:in `eval'
-e:1:in `<main>'
# caller #
/builddir/t/evaluator.rb:24:in `rescue in eval'
/builddir/t/evaluator.rb:21:in `eval'
-e:1:in `<main>'
RuntimeError: oops
I believe, that the exc.backtrace
is wrong and the second line should actually be /builddir/t/evaluator.rb:21:in `eval'
similarly to the output of the caller
.
Files
Updated by mame (Yusuke Endoh) about 4 years ago
- Status changed from Open to Rejected
It is confusing, but I think there is nothing wrong. There are two points.
The first point is that the location for a method written in C is represented by its caller's location.
This is an example: Integer#times
is implemented in C, so its location is represented as t.rb:2
.
1: def foo
2: 1.times do
3: raise
4: end
5: end
6:
7: foo
$ ./miniruby t.rb
t.rb:3:in `block in foo': unhandled exception
from t.rb:2:in `times' # <== See this line
from t.rb:2:in `foo'
from t.rb:7:in `<main>'
Note that there is no t.rb:1
location in the backtrace. A backtrace location represents where the interpreter is executing, so usually, the first line of a method body tend not to be included in a backtrace (unless an exception occurs during evaluation of optional arguments, for example).
The second point is that a rescue clause for a method body is considered as if the clause is called from the first line of the method body. This is exceptional, and may be confusing; it reflects the implementation detail. But currently it is at least intentional, I think.
This is an example: t.rb:4
is virtually called from t.rb:1
which is the first line of the method body.
def foo
raise
rescue
raise "foo", cause: nil
end
foo
$ ./miniruby t.rb
t.rb:4:in `rescue in foo': foo (RuntimeError)
from t.rb:1:in `foo' # <== See this line
from t.rb:7:in `<main>'
So, now your example can be understood as:
# exc.backtrace
-e:1:in `<main>'
/builddir/t/evaluator.rb:22:in `eval' # This line is for `Binding#eval`; the method is written in C, so it shows its caller's location
/builddir/t/evaluator.rb:22:in `eval' # This line is for `WebConsole::Evaluator#eval`; the interpreter now executes Line 22, so it is correct
-e:1:in `<main>'
# caller
/builddir/t/evaluator.rb:24:in `rescue in eval'
/builddir/t/evaluator.rb:21:in `eval' # This is included for the exception of a method rescue clause
-e:1:in `<main>'
Updated by vo.x (Vit Ondruch) about 4 years ago
I thought there will be some magic and yes, it is confusing, because I'd expect the rescue
exception to be the rule. Also, if the WebConsole::Evaluator#eval
was named differently, it would be probably more obvious. Appreciate your explanation. Thx.
Updated by vo.x (Vit Ondruch) almost 4 years ago
- Related to Bug #17419: `binding.eval` backtrace differente added