Skip to content

Commit

Permalink
REPL exception stack printing on error
Browse files Browse the repository at this point in the history
  • Loading branch information
c42f committed Nov 23, 2018
1 parent 8e924cb commit 074e39b
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 55 deletions.
24 changes: 22 additions & 2 deletions base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,27 @@ function display_error(io::IO, er, bt)
showerror(IOContext(io, :limit => true), er, bt)
println(io)
end
function display_error(io::IO, stack::ExceptionStack)
nexc = length(stack)
printstyled(io, "ERROR: "; bold=true, color=Base.error_color())
for i = nexc:-1:1
if nexc != i
printstyled(io, "caused by [exception ", i, "]\n", color=:light_black)
end
exc,bt = stack[i]
if bt != nothing
# remove REPL-related (or other) frames
eval_ind = findlast(addr->ip_matches_func(addr, :eval), bt)
if eval_ind !== nothing
bt = bt[1:eval_ind-1]
end
showerror(io, exc, bt, backtrace=bt!==nothing)
println(io)
end
end
end
display_error(er, bt) = display_error(stderr, er, bt)
display_error(er::ExceptionStack) = display_error(stderr, er)
display_error(er) = display_error(er, [])

function eval_user_input(@nospecialize(ast), show_value::Bool)
Expand Down Expand Up @@ -423,8 +443,8 @@ function _start()
@eval Main import Base.MainInclude: eval, include
try
exec_options(JLOptions())
catch err
invokelatest(display_error, err, catch_backtrace())
catch
invokelatest(display_error, current_exceptions())
exit(1)
end
if is_interactive && have_color
Expand Down
89 changes: 37 additions & 52 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const JULIA_PROMPT = "julia> "
mutable struct REPLBackend
"channel for AST"
repl_channel::Channel
"channel for results: (value, nothing) or (error, backtrace)"
"channel for results: (value, iserror)"
response_channel::Channel
"flag indicating the state of this backend"
in_eval::Bool
Expand All @@ -73,28 +73,28 @@ mutable struct REPLBackend
end

function eval_user_input(@nospecialize(ast), backend::REPLBackend)
iserr, lasterr = false, ((), nothing)
lasterr = nothing
Base.sigatomic_begin()
while true
try
Base.sigatomic_end()
if iserr
put!(backend.response_channel, lasterr)
if lasterr !== nothing
put!(backend.response_channel, (lasterr,true))
else
backend.in_eval = true
value = Core.eval(Main, ast)
backend.in_eval = false
# note: value wrapped carefully here to ensure it doesn't get passed through expand
ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :ans, value)
put!(backend.response_channel, (value, nothing))
put!(backend.response_channel, (value,false))
end
break
catch err
if iserr
if lasterr !== nothing
println("SYSTEM ERROR: Failed to report error to REPL frontend")
println(err)
end
iserr, lasterr = true, (err, catch_backtrace())
lasterr = current_exceptions()
end
end
Base.sigatomic_end()
Expand Down Expand Up @@ -134,20 +134,20 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x)
end
display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x)

function print_response(repl::AbstractREPL, @nospecialize(val), bt, show_value::Bool, have_color::Bool)
repl.waserror = bt !== nothing
function print_response(repl::AbstractREPL, @nospecialize(response), show_value::Bool, have_color::Bool)
repl.waserror = response[2]
io = IOContext(outstream(repl), :module => Main)
print_response(io, val, bt, show_value, have_color, specialdisplay(repl))
print_response(io, response, show_value, have_color, specialdisplay(repl))
nothing
end
function print_response(errio::IO, @nospecialize(val), bt, show_value::Bool, have_color::Bool, specialdisplay=nothing)
function print_response(errio::IO, @nospecialize(response), show_value::Bool, have_color::Bool, specialdisplay=nothing)
Base.sigatomic_begin()
val, iserr = response
while true
try
Base.sigatomic_end()
if bt !== nothing
Base.invokelatest(Base.display_error, errio, val, bt)
iserr, lasterr = false, ()
if iserr
Base.invokelatest(Base.display_error, errio, val)
else
if val !== nothing && show_value
try
Expand All @@ -163,15 +163,14 @@ function print_response(errio::IO, @nospecialize(val), bt, show_value::Bool, hav
end
end
break
catch err
if bt !== nothing
println(errio, "SYSTEM: show(lasterr) caused an error")
println(errio, err)
Base.show_backtrace(errio, bt)
catch
if iserr
println(errio, "SYSTEM (REPL): showing an error caused an error")
println(errio, current_exceptions())
break
end
val = err
bt = catch_backtrace()
val = current_exceptions()
iserr = true
end
end
Base.sigatomic_end()
Expand Down Expand Up @@ -207,7 +206,6 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
d = REPLDisplay(repl)
dopushdisplay = !in(d,Base.Multimedia.displays)
dopushdisplay && pushdisplay(d)
repl_channel, response_channel = backend.repl_channel, backend.response_channel
hit_eof = false
while true
Base.reseteof(repl.terminal)
Expand Down Expand Up @@ -238,17 +236,14 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
(isa(ast,Expr) && ast.head == :incomplete) || break
end
if !isempty(line)
put!(repl_channel, (ast, 1))
val, bt = take!(response_channel)
if !ends_with_semicolon(line)
print_response(repl, val, bt, true, false)
end
response = eval_with_backend(ast, backend)
print_response(repl, response, !ends_with_semicolon(line), false)
end
write(repl.terminal, '\n')
((!interrupted && isempty(line)) || hit_eof) && break
end
# terminate backend
put!(repl_channel, (nothing, -1))
put!(backend.repl_channel, (nothing, -1))
dopushdisplay && popdisplay(d)
nothing
end
Expand Down Expand Up @@ -686,12 +681,9 @@ find_hist_file() = get(ENV, "JULIA_HISTORY",

backend(r::AbstractREPL) = r.backendref

send_to_backend(ast, backend::REPLBackendRef) =
send_to_backend(ast, backend.repl_channel, backend.response_channel)

function send_to_backend(ast, req, rep)
put!(req, (ast, 1))
return take!(rep) # (val, bt)
function eval_with_backend(ast, backend::REPLBackendRef)
put!(backend.repl_channel, (ast, 1))
take!(backend.response_channel) # (val, iserr)
end

function respond(f, repl, main; pass_empty = false)
Expand All @@ -702,17 +694,14 @@ function respond(f, repl, main; pass_empty = false)
line = String(take!(buf))
if !isempty(line) || pass_empty
reset(repl)
local val, bt
local response
try
response = Base.invokelatest(f, line)
val, bt = send_to_backend(response, backend(repl))
catch err
val = err
bt = catch_backtrace()
end
if !ends_with_semicolon(line) || bt !== nothing
print_response(repl, val, bt, true, Base.have_color)
ast = Base.invokelatest(f, line)
response = eval_with_backend(ast, backend(repl))
catch
response = (current_exceptions(), true)
end
print_response(repl, response, !ends_with_semicolon(line), Base.have_color)
end
prepare_next(repl)
reset_state(s)
Expand Down Expand Up @@ -852,8 +841,8 @@ function setup_interface(
close(f)
end
hist_from_file(hp, f, hist_path)
catch e
print_response(repl, e, catch_backtrace(), true, Base.have_color)
catch
print_response(repl, (current_exceptions(),true), true, Base.have_color)
println(outstream(repl))
@info "Disabling history file for this session"
repl.history_file = false
Expand Down Expand Up @@ -1114,7 +1103,6 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
d = REPLDisplay(repl)
dopushdisplay = !in(d,Base.Multimedia.displays)
dopushdisplay && pushdisplay(d)
repl_channel, response_channel = backend.repl_channel, backend.response_channel
while !eof(repl.stream)
if have_color
print(repl.stream,repl.prompt_color)
Expand All @@ -1129,15 +1117,12 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
if have_color
print(repl.stream, Base.color_normal)
end
put!(repl_channel, (ast, 1))
val, bt = take!(response_channel)
if !ends_with_semicolon(line)
print_response(repl, val, bt, true, have_color)
end
response = eval_with_backend(ast, backend)
print_response(repl, response, !ends_with_semicolon(line), have_color)
end
end
# Terminate Backend
put!(repl_channel, (nothing, -1))
put!(backend.repl_channel, (nothing, -1))
dopushdisplay && popdisplay(d)
nothing
end
Expand Down
3 changes: 2 additions & 1 deletion stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,8 @@ mutable struct Error19864 <: Exception; end
function test19864()
@eval Base.showerror(io::IO, e::Error19864) = print(io, "correct19864")
buf = IOBuffer()
REPL.print_response(buf, Error19864(), [], false, false, nothing)
fake_response = (Base.ExceptionStack([(exception=Error19864(),backtrace=[])]),true)
REPL.print_response(buf, fake_response, false, false, nothing)
return String(take!(buf))
end
@test occursin("correct19864", test19864())
Expand Down

0 comments on commit 074e39b

Please sign in to comment.