Skip to content

Commit f4b3177

Browse files
vtjnashlazarusA
authored andcommitted
[REPL] improve quality of precompile script (JuliaLang#55210)
This rewrites the REPL precompile script to no longer rely upon ugly hacks for a cross-process copy and instead supports doing the precompile wholly within in the current process.
1 parent 2a5930a commit f4b3177

File tree

16 files changed

+197
-252
lines changed

16 files changed

+197
-252
lines changed

base/Base.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ delete_method(which(Pair{Any,Any}, (Any, Any)))
224224
end
225225

226226
# The REPL stdlib hooks into Base using this Ref
227-
const REPL_MODULE_REF = Ref{Module}()
227+
const REPL_MODULE_REF = Ref{Module}(Base)
228228

229229
include("checked.jl")
230230
using .Checked

base/client.jl

Lines changed: 78 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -416,78 +416,97 @@ function load_REPL()
416416
end
417417

418418
global active_repl
419+
global active_repl_backend = nothing
420+
421+
function run_fallback_repl(interactive::Bool)
422+
let input = stdin
423+
if isa(input, File) || isa(input, IOStream)
424+
# for files, we can slurp in the whole thing at once
425+
ex = parse_input_line(read(input, String))
426+
if Meta.isexpr(ex, :toplevel)
427+
# if we get back a list of statements, eval them sequentially
428+
# as if we had parsed them sequentially
429+
for stmt in ex.args
430+
eval_user_input(stderr, stmt, true)
431+
end
432+
body = ex.args
433+
else
434+
eval_user_input(stderr, ex, true)
435+
end
436+
else
437+
while !eof(input)
438+
if interactive
439+
print("julia> ")
440+
flush(stdout)
441+
end
442+
try
443+
line = ""
444+
ex = nothing
445+
while !eof(input)
446+
line *= readline(input, keep=true)
447+
ex = parse_input_line(line)
448+
if !(isa(ex, Expr) && ex.head === :incomplete)
449+
break
450+
end
451+
end
452+
eval_user_input(stderr, ex, true)
453+
catch err
454+
isa(err, InterruptException) ? print("\n\n") : rethrow()
455+
end
456+
end
457+
end
458+
end
459+
nothing
460+
end
461+
462+
function run_std_repl(REPL::Module, quiet::Bool, banner::Symbol, history_file::Bool)
463+
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
464+
term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr)
465+
banner == :no || REPL.banner(term, short=banner==:short)
466+
if term.term_type == "dumb"
467+
repl = REPL.BasicREPL(term)
468+
quiet || @warn "Terminal not fully functional"
469+
else
470+
repl = REPL.LineEditREPL(term, get(stdout, :color, false), true)
471+
repl.history_file = history_file
472+
end
473+
# Make sure any displays pushed in .julia/config/startup.jl ends up above the
474+
# REPLDisplay
475+
d = REPL.REPLDisplay(repl)
476+
last_active_repl = @isdefined(active_repl) ? active_repl : nothing
477+
last_active_repl_backend = active_repl_backend
478+
global active_repl = repl
479+
pushdisplay(d)
480+
try
481+
global active_repl = repl
482+
_atreplinit(repl)
483+
REPL.run_repl(repl, backend->(global active_repl_backend = backend))
484+
finally
485+
popdisplay(d)
486+
active_repl = last_active_repl
487+
active_repl_backend = last_active_repl_backend
488+
end
489+
nothing
490+
end
419491

420492
# run the requested sort of evaluation loop on stdio
421493
function run_main_repl(interactive::Bool, quiet::Bool, banner::Symbol, history_file::Bool)
422494
fallback_repl = parse(Bool, get(ENV, "JULIA_FALLBACK_REPL", "false"))
423495
if !fallback_repl && interactive
424496
load_InteractiveUtils()
425-
if !isassigned(REPL_MODULE_REF)
497+
REPL = REPL_MODULE_REF[]
498+
if REPL === Base
426499
load_REPL()
427500
end
428501
end
429-
# TODO cleanup REPL_MODULE_REF
430-
if !fallback_repl && interactive && isassigned(REPL_MODULE_REF)
431-
invokelatest(REPL_MODULE_REF[]) do REPL
432-
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
433-
term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr)
434-
banner == :no || REPL.banner(term, short=banner==:short)
435-
if term.term_type == "dumb"
436-
repl = REPL.BasicREPL(term)
437-
quiet || @warn "Terminal not fully functional"
438-
else
439-
repl = REPL.LineEditREPL(term, get(stdout, :color, false), true)
440-
repl.history_file = history_file
441-
end
442-
global active_repl = repl
443-
# Make sure any displays pushed in .julia/config/startup.jl ends up above the
444-
# REPLDisplay
445-
pushdisplay(REPL.REPLDisplay(repl))
446-
_atreplinit(repl)
447-
REPL.run_repl(repl, backend->(global active_repl_backend = backend))
448-
end
502+
REPL = REPL_MODULE_REF[]
503+
if !fallback_repl && interactive && REPL !== Base
504+
invokelatest(run_std_repl, REPL, quiet, banner, history_file)
449505
else
450-
# otherwise provide a simple fallback
451506
if !fallback_repl && interactive && !quiet
452507
@warn "REPL provider not available: using basic fallback" LOAD_PATH=join(Base.LOAD_PATH, Sys.iswindows() ? ';' : ':')
453508
end
454-
let input = stdin
455-
if isa(input, File) || isa(input, IOStream)
456-
# for files, we can slurp in the whole thing at once
457-
ex = parse_input_line(read(input, String))
458-
if Meta.isexpr(ex, :toplevel)
459-
# if we get back a list of statements, eval them sequentially
460-
# as if we had parsed them sequentially
461-
for stmt in ex.args
462-
eval_user_input(stderr, stmt, true)
463-
end
464-
body = ex.args
465-
else
466-
eval_user_input(stderr, ex, true)
467-
end
468-
else
469-
while !eof(input)
470-
if interactive
471-
print("julia> ")
472-
flush(stdout)
473-
end
474-
try
475-
line = ""
476-
ex = nothing
477-
while !eof(input)
478-
line *= readline(input, keep=true)
479-
ex = parse_input_line(line)
480-
if !(isa(ex, Expr) && ex.head === :incomplete)
481-
break
482-
end
483-
end
484-
eval_user_input(stderr, ex, true)
485-
catch err
486-
isa(err, InterruptException) ? print("\n\n") : rethrow()
487-
end
488-
end
489-
end
490-
end
509+
run_fallback_repl(interactive)
491510
end
492511
nothing
493512
end

base/docs/Docs.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,9 +610,8 @@ function docm(source::LineNumberNode, mod::Module, ex)
610610
@nospecialize ex
611611
if isexpr(ex, :->) && length(ex.args) > 1
612612
return docm(source, mod, ex.args...)
613-
elseif isassigned(Base.REPL_MODULE_REF)
613+
elseif (REPL = Base.REPL_MODULE_REF[]) !== Base
614614
# TODO: this is a shim to continue to allow `@doc` for looking up docstrings
615-
REPL = Base.REPL_MODULE_REF[]
616615
return invokelatest(REPL.lookup_doc, ex)
617616
else
618617
return simple_lookup_doc(ex)

base/loading.jl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,13 +1125,8 @@ function cache_file_entry(pkg::PkgId)
11251125
uuid === nothing ? pkg.name : package_slug(uuid)
11261126
end
11271127

1128-
# for use during running the REPL precompilation subprocess script, given we don't
1129-
# want it to pick up caches that already exist for other optimization levels
1130-
const ignore_compiled_cache = PkgId[]
1131-
11321128
function find_all_in_cache_path(pkg::PkgId, DEPOT_PATH::typeof(DEPOT_PATH)=DEPOT_PATH)
11331129
paths = String[]
1134-
pkg in ignore_compiled_cache && return paths
11351130
entrypath, entryfile = cache_file_entry(pkg)
11361131
for path in DEPOT_PATH
11371132
path = joinpath(path, entrypath)

base/show.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,8 +514,8 @@ function _show_default(io::IO, @nospecialize(x))
514514
end
515515

516516
function active_module()
517-
isassigned(REPL_MODULE_REF) || return Main
518517
REPL = REPL_MODULE_REF[]
518+
REPL === Base && return Main
519519
return invokelatest(REPL.active_module)::Module
520520
end
521521

base/task.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,7 @@ function task_done_hook(t::Task)
834834
end
835835

836836
if err && !handled && Threads.threadid() == 1
837-
if isa(result, InterruptException) && isdefined(Base, :active_repl_backend) &&
837+
if isa(result, InterruptException) && active_repl_backend !== nothing &&
838838
active_repl_backend.backend_task._state === task_state_runnable && isempty(Workqueue) &&
839839
active_repl_backend.in_eval
840840
throwto(active_repl_backend.backend_task, result) # this terminates the task
@@ -849,7 +849,7 @@ function task_done_hook(t::Task)
849849
# the exception to the REPL task since the current task is done.
850850
# issue #19467
851851
if Threads.threadid() == 1 &&
852-
isa(e, InterruptException) && isdefined(Base, :active_repl_backend) &&
852+
isa(e, InterruptException) && active_repl_backend !== nothing &&
853853
active_repl_backend.backend_task._state === task_state_runnable && isempty(Workqueue) &&
854854
active_repl_backend.in_eval
855855
throwto(active_repl_backend.backend_task, e)

stdlib/REPL/src/LineEdit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module LineEdit
44

55
import ..REPL
6-
using REPL: AbstractREPL, Options
6+
using ..REPL: AbstractREPL, Options
77

88
using ..Terminals
99
import ..Terminals: raw!, width, height, clear_line, beep

stdlib/REPL/src/REPL.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ include("options.jl")
128128

129129
include("LineEdit.jl")
130130
using .LineEdit
131-
import ..LineEdit:
131+
import .LineEdit:
132132
CompletionProvider,
133133
HistoryProvider,
134134
add_history,
@@ -760,6 +760,7 @@ struct LatexCompletions <: CompletionProvider end
760760

761761
function active_module() # this method is also called from Base
762762
isdefined(Base, :active_repl) || return Main
763+
Base.active_repl === nothing && return Main
763764
return active_module(Base.active_repl::AbstractREPL)
764765
end
765766
active_module((; mistate)::LineEditREPL) = mistate === nothing ? Main : mistate.active_module
@@ -1801,7 +1802,7 @@ module Numbered
18011802

18021803
using ..REPL
18031804

1804-
__current_ast_transforms() = isdefined(Base, :active_repl_backend) ? Base.active_repl_backend.ast_transforms : REPL.repl_ast_transforms
1805+
__current_ast_transforms() = Base.active_repl_backend !== nothing ? Base.active_repl_backend.ast_transforms : REPL.repl_ast_transforms
18051806

18061807
function repl_eval_counter(hp)
18071808
return length(hp.history) - hp.start_idx
@@ -1863,13 +1864,13 @@ end
18631864

18641865
function __current_ast_transforms(backend)
18651866
if backend === nothing
1866-
isdefined(Base, :active_repl_backend) ? Base.active_repl_backend.ast_transforms : REPL.repl_ast_transforms
1867+
Base.active_repl_backend !== nothing ? Base.active_repl_backend.ast_transforms : REPL.repl_ast_transforms
18671868
else
18681869
backend.ast_transforms
18691870
end
18701871
end
18711872

1872-
function numbered_prompt!(repl::LineEditREPL=Base.active_repl, backend=nothing)
1873+
function numbered_prompt!(repl::LineEditREPL=Base.active_repl::LineEditREPL, backend=nothing)
18731874
n = Ref{Int}(0)
18741875
set_prompt(repl, n)
18751876
set_output_prefix(repl, n)

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,10 @@ function maybe_spawn_cache_PATH()
295295
@lock PATH_cache_lock begin
296296
PATH_cache_task isa Task && !istaskdone(PATH_cache_task) && return
297297
time() < next_cache_update && return
298-
PATH_cache_task = Threads.@spawn REPLCompletions.cache_PATH()
298+
PATH_cache_task = Threads.@spawn begin
299+
REPLCompletions.cache_PATH()
300+
@lock PATH_cache_lock PATH_cache_task = nothing # release memory when done
301+
end
299302
Base.errormonitor(PATH_cache_task)
300303
end
301304
end

stdlib/REPL/src/TerminalMenus/TerminalMenus.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module TerminalMenus
44

5-
using REPL: REPL
5+
using ..REPL: REPL
66

77
function default_terminal(; in::IO=stdin, out::IO=stdout, err::IO=stderr)
88
return REPL.Terminals.TTYTerminal(

0 commit comments

Comments
 (0)