Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REPL: call display on the backend #57773

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 58 additions & 26 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ mutable struct REPLBackend
end
REPLBackend() = REPLBackend(Channel(1), Channel(1), false)

# A reference to a backend that is not mutable
struct REPLBackendRef
repl_channel::Channel{Any}
response_channel::Channel{Any}
end
REPLBackendRef(backend::REPLBackend) = REPLBackendRef(backend.repl_channel, backend.response_channel)

function destroy(ref::REPLBackendRef, state::Task)
if istaskfailed(state)
close(ref.repl_channel, TaskFailedException(state))
close(ref.response_channel, TaskFailedException(state))
end
close(ref.repl_channel)
close(ref.response_channel)
end

"""
softscope(ex)

Expand Down Expand Up @@ -418,12 +434,23 @@ function repl_backend_loop(backend::REPLBackend, get_module::Function)
while true
tls = task_local_storage()
tls[:SOURCE_PATH] = nothing
ast, show_value = take!(backend.repl_channel)
ast_or_func, show_value = take!(backend.repl_channel)
if show_value == -1
# exit flag
break
end
eval_user_input(ast, backend, get_module())
if show_value == 2 # 2 indicates a function to be called
f = ast_or_func
try
ret = f()
put!(backend.response_channel, Pair{Any, Bool}(ret, false))
catch err
put!(backend.response_channel, Pair{Any, Bool}(err, true))
end
else
ast = ast_or_func
eval_user_input(ast, backend, get_module())
end
end
return nothing
end
Expand Down Expand Up @@ -526,7 +553,7 @@ function print_response(repl::AbstractREPL, response, show_value::Bool, have_col
repl.waserror = response[2]
with_repl_linfo(repl) do io
io = IOContext(io, :module => Base.active_module(repl)::Module)
print_response(io, response, show_value, have_color, specialdisplay(repl))
print_response(io, response, backend(repl), show_value, have_color, specialdisplay(repl))
end
return nothing
end
Expand All @@ -543,7 +570,7 @@ function repl_display_error(errio::IO, @nospecialize errval)
return nothing
end

function print_response(errio::IO, response, show_value::Bool, have_color::Bool, specialdisplay::Union{AbstractDisplay,Nothing}=nothing)
function print_response(errio::IO, response, backend::Union{REPLBackendRef,Nothing}, show_value::Bool, have_color::Bool, specialdisplay::Union{AbstractDisplay,Nothing}=nothing)
Base.sigatomic_begin()
val, iserr = response
while true
Expand All @@ -555,15 +582,19 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool,
repl_display_error(errio, val)
else
if val !== nothing && show_value
try
if specialdisplay === nothing
val2, iserr = if specialdisplay === nothing
# display calls may require being run on the main thread
eval_with_backend(backend) do
Base.invokelatest(display, val)
else
end
else
eval_with_backend(backend) do
Base.invokelatest(display, specialdisplay, val)
end
catch
end
if iserr
println(errio, "Error showing value of type ", typeof(val), ":")
rethrow()
throw(val2)
end
end
end
Expand Down Expand Up @@ -593,21 +624,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool,
nothing
end

# A reference to a backend that is not mutable
struct REPLBackendRef
repl_channel::Channel{Any}
response_channel::Channel{Any}
end
REPLBackendRef(backend::REPLBackend) = REPLBackendRef(backend.repl_channel, backend.response_channel)

function destroy(ref::REPLBackendRef, state::Task)
if istaskfailed(state)
close(ref.repl_channel, TaskFailedException(state))
close(ref.response_channel, TaskFailedException(state))
end
close(ref.repl_channel)
close(ref.response_channel)
end

"""
run_repl(repl::AbstractREPL)
Expand Down Expand Up @@ -1128,12 +1145,27 @@ find_hist_file() = get(ENV, "JULIA_HISTORY",
!isempty(DEPOT_PATH) ? joinpath(DEPOT_PATH[1], "logs", "repl_history.jl") :
error("DEPOT_PATH is empty and ENV[\"JULIA_HISTORY\"] not set."))

backend(r::AbstractREPL) = r.backendref
backend(r::AbstractREPL) = hasproperty(r, :backendref) ? r.backendref : nothing

function eval_with_backend(ast, backend::REPLBackendRef)
put!(backend.repl_channel, (ast, 1))

function eval_with_backend(ast::Expr, backend::REPLBackendRef)
put!(backend.repl_channel, (ast, 1)) # (f, show_value)
return take!(backend.response_channel) # (val, iserr)
end
function eval_with_backend(f, backend::REPLBackendRef)
put!(backend.repl_channel, (f, 2)) # (f, show_value) 2 indicates function (rather than ast)
return take!(backend.response_channel) # (val, iserr)
end
# if no backend just eval (used by tests)
function eval_with_backend(f, backend::Nothing)
try
ret = f()
return (ret, false) # (val, iserr)
catch err
return (err, true)
end
end


function respond(f, repl, main; pass_empty::Bool = false, suppress_on_semicolon::Bool = true)
return function do_respond(s::MIState, buf, ok::Bool)
Expand Down
2 changes: 1 addition & 1 deletion stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ function test19864()
@eval Base.showerror(io::IO, e::Error19864) = print(io, "correct19864")
buf = IOBuffer()
fake_response = (Base.ExceptionStack([(exception=Error19864(),backtrace=Ptr{Cvoid}[])]),true)
REPL.print_response(buf, fake_response, false, false, nothing)
REPL.print_response(buf, fake_response, nothing, false, false, nothing)
return String(take!(buf))
end
@test occursin("correct19864", test19864())
Expand Down