StatProfilerHTML.jl report
Generated on Mon, 01 Apr 2024 21:01:18
File source code
Line Exclusive Inclusive Code
1 # This file is a part of Julia. License is MIT: https://julialang.org/license
2
3 ## client.jl - frontend handling command line options, environment setup,
4 ## and REPL
5
6 have_color = nothing
7 const default_color_warn = :yellow
8 const default_color_error = :light_red
9 const default_color_info = :cyan
10 const default_color_debug = :blue
11 const default_color_input = :normal
12 const default_color_answer = :normal
13 const color_normal = text_colors[:normal]
14
15 function repl_color(key, default)
16 env_str = get(ENV, key, "")
17 c = tryparse(Int, env_str)
18 c_conv = something(c, Symbol(env_str))
19 haskey(text_colors, c_conv) ? c_conv : default
20 end
21
22 error_color() = repl_color("JULIA_ERROR_COLOR", default_color_error)
23 warn_color() = repl_color("JULIA_WARN_COLOR" , default_color_warn)
24 info_color() = repl_color("JULIA_INFO_COLOR" , default_color_info)
25 debug_color() = repl_color("JULIA_DEBUG_COLOR" , default_color_debug)
26
27 input_color() = text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)]
28 answer_color() = text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)]
29
30 stackframe_lineinfo_color() = repl_color("JULIA_STACKFRAME_LINEINFO_COLOR", :bold)
31 stackframe_function_color() = repl_color("JULIA_STACKFRAME_FUNCTION_COLOR", :bold)
32
33 function repl_cmd(cmd, out)
34 shell = shell_split(get(ENV, "JULIA_SHELL", get(ENV, "SHELL", "/bin/sh")))
35 shell_name = Base.basename(shell[1])
36
37 # Immediately expand all arguments, so that typing e.g. ~/bin/foo works.
38 cmd.exec .= expanduser.(cmd.exec)
39
40 if isempty(cmd.exec)
41 throw(ArgumentError("no cmd to execute"))
42 elseif cmd.exec[1] == "cd"
43 new_oldpwd = pwd()
44 if length(cmd.exec) > 2
45 throw(ArgumentError("cd method only takes one argument"))
46 elseif length(cmd.exec) == 2
47 dir = cmd.exec[2]
48 if dir == "-"
49 if !haskey(ENV, "OLDPWD")
50 error("cd: OLDPWD not set")
51 end
52 dir = ENV["OLDPWD"]
53 end
54 cd(dir)
55 else
56 cd()
57 end
58 ENV["OLDPWD"] = new_oldpwd
59 println(out, pwd())
60 else
61 @static if !Sys.iswindows()
62 if shell_name == "fish"
63 shell_escape_cmd = "begin; $(shell_escape_posixly(cmd)); and true; end"
64 else
65 shell_escape_cmd = "($(shell_escape_posixly(cmd))) && true"
66 end
67 cmd = `$shell -c $shell_escape_cmd`
68 end
69 try
70 run(ignorestatus(cmd))
71 catch
72 # Windows doesn't shell out right now (complex issue), so Julia tries to run the program itself
73 # Julia throws an exception if it can't find the program, but the stack trace isn't useful
74 lasterr = current_exceptions()
75 lasterr = ExceptionStack([(exception = e[1], backtrace = [] ) for e in lasterr])
76 invokelatest(display_error, lasterr)
77 end
78 end
79 nothing
80 end
81
82 # deprecated function--preserved for DocTests.jl
83 function ip_matches_func(ip, func::Symbol)
84 for fr in StackTraces.lookup(ip)
85 if fr === StackTraces.UNKNOWN || fr.from_c
86 return false
87 end
88 fr.func === func && return true
89 end
90 return false
91 end
92
93 function scrub_repl_backtrace(bt)
94 if bt !== nothing && !(bt isa Vector{Any}) # ignore our sentinel value types
95 bt = bt isa Vector{StackFrame} ? copy(bt) : stacktrace(bt)
96 # remove REPL-related frames from interactive printing
97 eval_ind = findlast(frame -> !frame.from_c && frame.func === :eval, bt)
98 eval_ind === nothing || deleteat!(bt, eval_ind:length(bt))
99 end
100 return bt
101 end
102 scrub_repl_backtrace(stack::ExceptionStack) =
103 ExceptionStack(Any[(;x.exception, backtrace = scrub_repl_backtrace(x.backtrace)) for x in stack])
104
105 istrivialerror(stack::ExceptionStack) =
106 length(stack) == 1 && length(stack[1].backtrace) ≤ 1 && !isa(stack[1].exception, MethodError)
107 # frame 1 = top level; assumes already went through scrub_repl_backtrace; MethodError see #50803
108
109 function display_error(io::IO, stack::ExceptionStack)
110 printstyled(io, "ERROR: "; bold=true, color=Base.error_color())
111 show_exception_stack(IOContext(io, :limit => true), stack)
112 println(io)
113 end
114 display_error(stack::ExceptionStack) = display_error(stderr, stack)
115
116 # these forms are depended on by packages outside Julia
117 function display_error(io::IO, er, bt)
118 printstyled(io, "ERROR: "; bold=true, color=Base.error_color())
119 showerror(IOContext(io, :limit => true), er, bt, backtrace = bt!==nothing)
120 println(io)
121 end
122 display_error(er, bt=nothing) = display_error(stderr, er, bt)
123
124 function eval_user_input(errio, @nospecialize(ast), show_value::Bool)
125 errcount = 0
126 lasterr = nothing
127 have_color = get(stdout, :color, false)::Bool
128 while true
129 try
130 if have_color
131 print(color_normal)
132 end
133 if lasterr !== nothing
134 lasterr = scrub_repl_backtrace(lasterr)
135 istrivialerror(lasterr) || setglobal!(Base.MainInclude, :err, lasterr)
136 invokelatest(display_error, errio, lasterr)
137 errcount = 0
138 lasterr = nothing
139 else
140 ast = Meta.lower(Main, ast)
141 value = Core.eval(Main, ast)
142 setglobal!(Base.MainInclude, :ans, value)
143 if !(value === nothing) && show_value
144 if have_color
145 print(answer_color())
146 end
147 try
148 invokelatest(display, value)
149 catch
150 @error "Evaluation succeeded, but an error occurred while displaying the value" typeof(value)
151 rethrow()
152 end
153 end
154 end
155 break
156 catch
157 if errcount > 0
158 @error "SYSTEM: display_error(errio, lasterr) caused an error"
159 end
160 errcount += 1
161 lasterr = scrub_repl_backtrace(current_exceptions())
162 setglobal!(Base.MainInclude, :err, lasterr)
163 if errcount > 2
164 @error "It is likely that something important is broken, and Julia will not be able to continue normally" errcount
165 break
166 end
167 end
168 end
169 isa(stdin, TTY) && println()
170 nothing
171 end
172
173 function _parse_input_line_core(s::String, filename::String)
174 ex = Meta.parseall(s, filename=filename)
175 if ex isa Expr && ex.head === :toplevel
176 if isempty(ex.args)
177 return nothing
178 end
179 last = ex.args[end]
180 if last isa Expr && (last.head === :error || last.head === :incomplete)
181 # if a parse error happens in the middle of a multi-line input
182 # return only the error, so that none of the input is evaluated.
183 return last
184 end
185 end
186 return ex
187 end
188
189 function parse_input_line(s::String; filename::String="none", depwarn=true)
190 # For now, assume all parser warnings are depwarns
191 ex = if depwarn
192 _parse_input_line_core(s, filename)
193 else
194 with_logger(NullLogger()) do
195 _parse_input_line_core(s, filename)
196 end
197 end
198 return ex
199 end
200 parse_input_line(s::AbstractString) = parse_input_line(String(s))
201
202 # detect the reason which caused an :incomplete expression
203 # from the error message
204 # NOTE: the error messages are defined in src/julia-parser.scm
205 function fl_incomplete_tag(msg::AbstractString)
206 occursin("string", msg) && return :string
207 occursin("comment", msg) && return :comment
208 occursin("requires end", msg) && return :block
209 occursin("\"`\"", msg) && return :cmd
210 occursin("character", msg) && return :char
211 return :other
212 end
213
214 incomplete_tag(ex) = :none
215 function incomplete_tag(ex::Expr)
216 if ex.head !== :incomplete
217 return :none
218 elseif isempty(ex.args)
219 return :other
220 elseif ex.args[1] isa String
221 return fl_incomplete_tag(ex.args[1])
222 else
223 return incomplete_tag(ex.args[1])
224 end
225 end
226 incomplete_tag(exc::Meta.ParseError) = incomplete_tag(exc.detail)
227
228
58 (100 %) samples spent in exec_options
58 (100 %) (incl.) when called from _start line 552
function exec_options(opts)
229 quiet = (opts.quiet != 0)
230 startup = (opts.startupfile != 2)
231 history_file = (opts.historyfile != 0)
232 color_set = (opts.color != 0) # --color!=auto
233 global have_color = color_set ? (opts.color == 1) : nothing # --color=on
234 global is_interactive = (opts.isinteractive != 0)
235
236 # pre-process command line argument list
237 arg_is_program = !isempty(ARGS)
238 repl = !arg_is_program
239 cmds = unsafe_load_commands(opts.commands)
240 for (cmd, arg) in cmds
241 if cmd == 'e'
242 arg_is_program = false
243 repl = false
244 elseif cmd == 'E'
245 arg_is_program = false
246 repl = false
247 elseif cmd == 'L'
248 # nothing
249 elseif cmd == 'B' # --bug-report
250 # If we're doing a bug report, don't load anything else. We will
251 # spawn a child in which to execute these options.
252 let InteractiveUtils = load_InteractiveUtils()
253 InteractiveUtils.report_bug(arg)
254 end
255 return nothing
256 else
257 @warn "Unexpected command -$cmd'$arg'"
258 end
259 end
260
261 # remove filename from ARGS
262 global PROGRAM_FILE = arg_is_program ? popfirst!(ARGS) : ""
263
264 # Load Distributed module only if any of the Distributed options have been specified.
265 distributed_mode = (opts.worker == 1) || (opts.nprocs > 0) || (opts.machine_file != C_NULL)
266 if distributed_mode
267 let Distributed = require(PkgId(UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed"))
268 Core.eval(Main, :(const Distributed = $Distributed))
269 Core.eval(Main, :(using .Distributed))
270 end
271
272 invokelatest(Main.Distributed.process_opts, opts)
273 end
274
275 interactiveinput = (repl || is_interactive::Bool) && isa(stdin, TTY)
276 is_interactive::Bool |= interactiveinput
277
278 # load ~/.julia/config/startup.jl file
279 if startup
280 try
281 load_julia_startup()
282 catch
283 invokelatest(display_error, scrub_repl_backtrace(current_exceptions()))
284 !(repl || is_interactive::Bool) && exit(1)
285 end
286 end
287
288 # process cmds list
289 for (cmd, arg) in cmds
290 if cmd == 'e'
291 Core.eval(Main, parse_input_line(arg))
292 elseif cmd == 'E'
293 invokelatest(show, Core.eval(Main, parse_input_line(arg)))
294 println()
295 elseif cmd == 'L'
296 # load file immediately on all processors
297 if !distributed_mode
298 include(Main, arg)
299 else
300 # TODO: Move this logic to Distributed and use a callback
301 @sync for p in invokelatest(Main.procs)
302 @async invokelatest(Main.remotecall_wait, include, p, Main, arg)
303 end
304 end
305 end
306 end
307
308 # load file
309 if arg_is_program
310 # program
311 if !is_interactive::Bool
312 exit_on_sigint(true)
313 end
314 try
315 if PROGRAM_FILE == "-"
316 include_string(Main, read(stdin, String), "stdin")
317 else
318 include(Main, PROGRAM_FILE)
319 end
320 catch
321 invokelatest(display_error, scrub_repl_backtrace(current_exceptions()))
322 if !is_interactive::Bool
323 exit(1)
324 end
325 end
326 end
327 if repl || is_interactive::Bool
328 if interactiveinput
329 banner = (opts.banner != 0) # --banner!=no
330 else
331 banner = (opts.banner == 1) # --banner=yes
332 end
333 58 (100 %)
58 (100 %) samples spent calling run_main_repl
run_main_repl(interactiveinput, quiet, banner, history_file, color_set)
334 end
335 nothing
336 end
337
338 function _global_julia_startup_file()
339 # If the user built us with a specific Base.SYSCONFDIR, check that location first for a startup.jl file
340 # If it is not found, then continue on to the relative path based on Sys.BINDIR
341 BINDIR = Sys.BINDIR
342 SYSCONFDIR = Base.SYSCONFDIR
343 if !isempty(SYSCONFDIR)
344 p1 = abspath(BINDIR, SYSCONFDIR, "julia", "startup.jl")
345 isfile(p1) && return p1
346 end
347 p2 = abspath(BINDIR, "..", "etc", "julia", "startup.jl")
348 isfile(p2) && return p2
349 return nothing
350 end
351
352 function _local_julia_startup_file()
353 if !isempty(DEPOT_PATH)
354 path = abspath(DEPOT_PATH[1], "config", "startup.jl")
355 isfile(path) && return path
356 end
357 return nothing
358 end
359
360 function load_julia_startup()
361 global_file = _global_julia_startup_file()
362 (global_file !== nothing) && include(Main, global_file)
363 local_file = _local_julia_startup_file()
364 (local_file !== nothing) && include(Main, local_file)
365 return nothing
366 end
367
368 const repl_hooks = []
369
370 """
371 atreplinit(f)
372
373 Register a one-argument function to be called before the REPL interface is initialized in
374 interactive sessions; this is useful to customize the interface. The argument of `f` is the
375 REPL object. This function should be called from within the `.julia/config/startup.jl`
376 initialization file.
377 """
378 atreplinit(f::Function) = (pushfirst!(repl_hooks, f); nothing)
379
380 function __atreplinit(repl)
381 for f in repl_hooks
382 try
383 f(repl)
384 catch err
385 showerror(stderr, err)
386 println(stderr)
387 end
388 end
389 end
390 _atreplinit(repl) = invokelatest(__atreplinit, repl)
391
392 function load_InteractiveUtils(mod::Module=Main)
393 # load interactive-only libraries
394 if !isdefined(mod, :InteractiveUtils)
395 try
396 let InteractiveUtils = require(PkgId(UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils"))
397 Core.eval(mod, :(const InteractiveUtils = $InteractiveUtils))
398 Core.eval(mod, :(using .InteractiveUtils))
399 return InteractiveUtils
400 end
401 catch ex
402 @warn "Failed to import InteractiveUtils into module $mod" exception=(ex, catch_backtrace())
403 end
404 return nothing
405 end
406 return getfield(mod, :InteractiveUtils)
407 end
408
409 global active_repl
410
411 # run the requested sort of evaluation loop on stdio
412
58 (100 %) samples spent in run_main_repl
58 (100 %) (incl.) when called from exec_options line 333
function run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, color_set::Bool)
413 load_InteractiveUtils()
414
415 if interactive && isassigned(REPL_MODULE_REF)
416 58 (100 %)
58 (100 %) samples spent calling invokelatest
invokelatest(REPL_MODULE_REF[]) do REPL
417
58 (100 %) samples spent in #1013
58 (100 %) (incl.) when called from #invokelatest#2 line 892
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
418 term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr)
419 banner && Base.banner(term)
420 if term.term_type == "dumb"
421 repl = REPL.BasicREPL(term)
422 quiet || @warn "Terminal not fully functional"
423 else
424 repl = REPL.LineEditREPL(term, get(stdout, :color, false), true)
425 repl.history_file = history_file
426 end
427 global active_repl = repl
428 # Make sure any displays pushed in .julia/config/startup.jl ends up above the
429 # REPLDisplay
430 pushdisplay(REPL.REPLDisplay(repl))
431 _atreplinit(repl)
432 58 (100 %)
58 (100 %) samples spent calling run_repl
REPL.run_repl(repl, backend->(global active_repl_backend = backend))
433 end
434 else
435 # otherwise provide a simple fallback
436 if interactive && !quiet
437 @warn "REPL provider not available: using basic fallback"
438 end
439 banner && Base.banner()
440 let input = stdin
441 if isa(input, File) || isa(input, IOStream)
442 # for files, we can slurp in the whole thing at once
443 ex = parse_input_line(read(input, String))
444 if Meta.isexpr(ex, :toplevel)
445 # if we get back a list of statements, eval them sequentially
446 # as if we had parsed them sequentially
447 for stmt in ex.args
448 eval_user_input(stderr, stmt, true)
449 end
450 body = ex.args
451 else
452 eval_user_input(stderr, ex, true)
453 end
454 else
455 while isopen(input) || !eof(input)
456 if interactive
457 print("julia> ")
458 flush(stdout)
459 end
460 try
461 line = ""
462 ex = nothing
463 while !eof(input)
464 line *= readline(input, keep=true)
465 ex = parse_input_line(line)
466 if !(isa(ex, Expr) && ex.head === :incomplete)
467 break
468 end
469 end
470 eval_user_input(stderr, ex, true)
471 catch err
472 isa(err, InterruptException) ? print("\n\n") : rethrow()
473 end
474 end
475 end
476 end
477 end
478 nothing
479 end
480
481 # MainInclude exists to hide Main.include and eval from `names(Main)`.
482 baremodule MainInclude
483 using ..Base
484 # These definitions calls Base._include rather than Base.include to get
485 # one-frame stacktraces for the common case of using include(fname) in Main.
486 include(mapexpr::Function, fname::AbstractString) = Base._include(mapexpr, Main, fname)
487
58 (100 %) samples spent in include
58 (100 %) (incl.) when called from eval line 385
function include(fname::AbstractString)
488 isa(fname, String) || (fname = Base.convert(String, fname)::String)
489 58 (100 %)
58 (100 %) samples spent calling _include
Base._include(identity, Main, fname)
490 end
491 eval(x) = Core.eval(Main, x)
492
493 """
494 ans
495
496 A variable referring to the last computed value, automatically imported to the interactive prompt.
497 """
498 global ans = nothing
499
500 """
501 err
502
503 A variable referring to the last thrown errors, automatically imported to the interactive prompt.
504 The thrown errors are collected in a stack of exceptions.
505 """
506 global err = nothing
507
508 # weakly exposes ans and err variables to Main
509 export ans, err
510
511 end
512
513 """
514 eval(expr)
515
516 Evaluate an expression in the global scope of the containing module.
517 Every `Module` (except those defined with `baremodule`) has its own 1-argument
518 definition of `eval`, which evaluates expressions in that module.
519 """
520 MainInclude.eval
521
522 """
523 include([mapexpr::Function,] path::AbstractString)
524
525 Evaluate the contents of the input source file in the global scope of the containing module.
526 Every module (except those defined with `baremodule`) has its own
527 definition of `include`, which evaluates the file in that module.
528 Returns the result of the last evaluated expression of the input file. During including,
529 a task-local include path is set to the directory containing the file. Nested calls to
530 `include` will search relative to that path. This function is typically used to load source
531 interactively, or to combine files in packages that are broken into multiple source files.
532 The argument `path` is normalized using [`normpath`](@ref) which will resolve
533 relative path tokens such as `..` and convert `/` to the appropriate path separator.
534
535 The optional first argument `mapexpr` can be used to transform the included code before
536 it is evaluated: for each parsed expression `expr` in `path`, the `include` function
537 actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`identity`](@ref).
538
539 Use [`Base.include`](@ref) to evaluate a file into another module.
540
541 !!! compat "Julia 1.5"
542 Julia 1.5 is required for passing the `mapexpr` argument.
543 """
544 MainInclude.include
545
546 function _start()
547 empty!(ARGS)
548 append!(ARGS, Core.ARGS)
549 # clear any postoutput hooks that were saved in the sysimage
550 empty!(Base.postoutput_hooks)
551 try
552 58 (100 %)
58 (100 %) samples spent calling exec_options
exec_options(JLOptions())
553 catch
554 invokelatest(display_error, scrub_repl_backtrace(current_exceptions()))
555 exit(1)
556 end
557 if is_interactive && get(stdout, :color, false)
558 print(color_normal)
559 end
560 end