StatProfilerHTML.jl report
Generated on tor 10 okt 2019 11:38:33
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 = false
7 default_color_warn = :yellow
8 default_color_error = :light_red
9 default_color_info = :cyan
10 default_color_debug = :blue
11 default_color_input = :normal
12 default_color_answer = :normal
13 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 cd(ENV["OLDPWD"])
53 else
54 @static if !Sys.iswindows()
55 # TODO: this is a rather expensive way to copy a string, remove?
56 # If it's intended to simulate `cd`, it should instead be doing
57 # more nearly `cd $dir && printf %s \$PWD` (with appropriate quoting),
58 # since shell `cd` does more than just `echo` the result.
59 dir = read(`$shell -c "printf '%s' $(shell_escape_posixly(dir))"`, String)
60 end
61 cd(dir)
62 end
63 else
64 cd()
65 end
66 ENV["OLDPWD"] = new_oldpwd
67 println(out, pwd())
68 else
69 @static if !Sys.iswindows()
70 if shell_name == "fish"
71 shell_escape_cmd = "begin; $(shell_escape_posixly(cmd)); and true; end"
72 else
73 shell_escape_cmd = "($(shell_escape_posixly(cmd))) && true"
74 end
75 cmd = `$shell -c $shell_escape_cmd`
76 end
77 run(ignorestatus(cmd))
78 end
79 nothing
80 end
81
82 function ip_matches_func(ip, func::Symbol)
83 for fr in StackTraces.lookup(ip)
84 if fr === StackTraces.UNKNOWN || fr.from_c
85 return false
86 end
87 fr.func === func && return true
88 end
89 return false
90 end
91
92 function display_error(io::IO, er, bt)
93 printstyled(io, "ERROR: "; bold=true, color=Base.error_color())
94 # remove REPL-related frames from interactive printing
95 eval_ind = findlast(addr->ip_matches_func(addr, :eval), bt)
96 if eval_ind !== nothing
97 bt = bt[1:eval_ind-1]
98 end
99 showerror(IOContext(io, :limit => true), er, bt)
100 println(io)
101 end
102 display_error(er, bt) = display_error(stderr, er, bt)
103 display_error(er) = display_error(er, [])
104
105 function eval_user_input(@nospecialize(ast), show_value::Bool)
106 errcount, lasterr, bt = 0, (), nothing
107 while true
108 try
109 if have_color
110 print(color_normal)
111 end
112 if errcount > 0
113 invokelatest(display_error, lasterr, bt)
114 errcount, lasterr = 0, ()
115 else
116 ast = Meta.lower(Main, ast)
117 value = Core.eval(Main, ast)
118 ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :ans, value)
119 if !(value === nothing) && show_value
120 if have_color
121 print(answer_color())
122 end
123 try
124 invokelatest(display, value)
125 catch err
126 println(stderr, "Evaluation succeeded, but an error occurred while showing value of type ", typeof(value), ":")
127 rethrow(err)
128 end
129 println()
130 end
131 end
132 break
133 catch err
134 if errcount > 0
135 println(stderr, "SYSTEM: show(lasterr) caused an error")
136 end
137 errcount, lasterr = errcount+1, err
138 if errcount > 2
139 println(stderr, "WARNING: it is likely that something important is broken, and Julia will not be able to continue normally")
140 break
141 end
142 bt = catch_backtrace()
143 end
144 end
145 isa(stdin, TTY) && println()
146 nothing
147 end
148
149 function parse_input_line(s::String; filename::String="none", depwarn=true)
150 # For now, assume all parser warnings are depwarns
151 ex = if depwarn
152 ccall(:jl_parse_input_line, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t),
153 s, sizeof(s), filename, sizeof(filename))
154 else
155 with_logger(NullLogger()) do
156 ccall(:jl_parse_input_line, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t),
157 s, sizeof(s), filename, sizeof(filename))
158 end
159 end
160 return ex
161 end
162 parse_input_line(s::AbstractString) = parse_input_line(String(s))
163
164 function parse_input_line(io::IO)
165 s = ""
166 while !eof(io)
167 s *= readline(io, keep=true)
168 e = parse_input_line(s)
169 if !(isa(e,Expr) && e.head === :incomplete)
170 return e
171 end
172 end
173 end
174
175 # detect the reason which caused an :incomplete expression
176 # from the error message
177 # NOTE: the error messages are defined in src/julia-parser.scm
178 incomplete_tag(ex) = :none
179 function incomplete_tag(ex::Expr)
180 Meta.isexpr(ex, :incomplete) || return :none
181 msg = ex.args[1]
182 occursin("string", msg) && return :string
183 occursin("comment", msg) && return :comment
184 occursin("requires end", msg) && return :block
185 occursin("\"`\"", msg) && return :cmd
186 occursin("character", msg) && return :char
187 return :other
188 end
189
190 # call include() on a file, ignoring if not found
191 include_ifexists(mod::Module, path::AbstractString) = isfile(path) && include(mod, path)
192
193
40 (95.24%) samples spent in exec_options
0 (ex.), 40 (100.00%) (incl.) when called from _start line 425
function exec_options(opts)
194 if !isempty(ARGS)
195 idxs = findall(x -> x == "--", ARGS)
196 length(idxs) > 0 && deleteat!(ARGS, idxs[1])
197 end
198 quiet = (opts.quiet != 0)
199 startup = (opts.startupfile != 2)
200 history_file = (opts.historyfile != 0)
201 color_set = (opts.color != 0) # --color!=auto
202 global have_color = (opts.color == 1) # --color=on
203 global is_interactive = (opts.isinteractive != 0)
204
205 # pre-process command line argument list
206 arg_is_program = !isempty(ARGS)
207 repl = !arg_is_program
208 cmds = unsafe_load_commands(opts.commands)
209 for (cmd, arg) in cmds
210 if cmd == 'e'
211 arg_is_program = false
212 repl = false
213 elseif cmd == 'E'
214 arg_is_program = false
215 repl = false
216 elseif cmd == 'L'
217 # nothing
218 else
219 @warn "Unexpected command -$cmd'$arg'"
220 end
221 end
222
223 # remove filename from ARGS
224 global PROGRAM_FILE = arg_is_program ? popfirst!(ARGS) : ""
225
226 # Load Distributed module only if any of the Distributed options have been specified.
227 distributed_mode = (opts.worker == 1) || (opts.nprocs > 0) || (opts.machine_file != C_NULL)
228 if distributed_mode
229 let Distributed = require(PkgId(UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed"))
230 Core.eval(Main, :(const Distributed = $Distributed))
231 Core.eval(Main, :(using .Distributed))
232 end
233
234 invokelatest(Main.Distributed.process_opts, opts)
235 end
236
237 # load ~/.julia/config/startup.jl file
238 startup && load_julia_startup()
239
240 # process cmds list
241 for (cmd, arg) in cmds
242 if cmd == 'e'
243 Core.eval(Main, parse_input_line(arg))
244 elseif cmd == 'E'
245 invokelatest(show, Core.eval(Main, parse_input_line(arg)))
246 println()
247 elseif cmd == 'L'
248 # load file immediately on all processors
249 if !distributed_mode
250 include(Main, arg)
251 else
252 # TODO: Move this logic to Distributed and use a callback
253 @sync for p in invokelatest(Main.procs)
254 @async invokelatest(Main.remotecall_wait, include, p, Main, arg)
255 end
256 end
257 end
258 end
259
260 # load file
261 if arg_is_program
262 # program
263 if !is_interactive
264 ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 1)
265 end
266 40 (95.24%)
40 (100.00%) samples spent calling include
include(Main, PROGRAM_FILE)
267 end
268 repl |= is_interactive
269 if repl
270 interactiveinput = isa(stdin, TTY)
271 if interactiveinput
272 global is_interactive = true
273 banner = (opts.banner != 0) # --banner!=no
274 else
275 banner = (opts.banner == 1) # --banner=yes
276 end
277 run_main_repl(interactiveinput, quiet, banner, history_file, color_set)
278 end
279 nothing
280 end
281
282 function load_julia_startup()
283 # If the user built us with a specific Base.SYSCONFDIR, check that location first for a startup.jl file
284 # If it is not found, then continue on to the relative path based on Sys.BINDIR
285 BINDIR = Sys.BINDIR::String
286 SYSCONFDIR = Base.SYSCONFDIR::String
287 if !isempty(SYSCONFDIR) && isfile(joinpath(BINDIR, SYSCONFDIR, "julia", "startup.jl"))
288 include(Main, abspath(BINDIR, SYSCONFDIR, "julia", "startup.jl"))
289 else
290 include_ifexists(Main, abspath(BINDIR, "..", "etc", "julia", "startup.jl"))
291 end
292 include_ifexists(Main, abspath(homedir(), ".julia", "config", "startup.jl"))
293 return nothing
294 end
295
296 const repl_hooks = []
297
298 """
299 atreplinit(f)
300
301 Register a one-argument function to be called before the REPL interface is initialized in
302 interactive sessions; this is useful to customize the interface. The argument of `f` is the
303 REPL object. This function should be called from within the `.julia/config/startup.jl`
304 initialization file.
305 """
306 atreplinit(f::Function) = (pushfirst!(repl_hooks, f); nothing)
307
308 function __atreplinit(repl)
309 for f in repl_hooks
310 try
311 f(repl)
312 catch err
313 showerror(stderr, err)
314 println(stderr)
315 end
316 end
317 end
318 _atreplinit(repl) = invokelatest(__atreplinit, repl)
319
320 # The REPL stdlib hooks into Base using this Ref
321 const REPL_MODULE_REF = Ref{Module}()
322
323 # run the requested sort of evaluation loop on stdio
324 function run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, color_set::Bool)
325 global active_repl
326 # load interactive-only libraries
327 if !isdefined(Main, :InteractiveUtils)
328 try
329 let InteractiveUtils = require(PkgId(UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils"))
330 Core.eval(Main, :(const InteractiveUtils = $InteractiveUtils))
331 Core.eval(Main, :(using .InteractiveUtils))
332 end
333 catch ex
334 @warn "Failed to import InteractiveUtils into module Main" exception=(ex, catch_backtrace())
335 end
336 end
337
338 if interactive && isassigned(REPL_MODULE_REF)
339 invokelatest(REPL_MODULE_REF[]) do REPL
340 term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
341 term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr)
342 color_set || (global have_color = REPL.Terminals.hascolor(term))
343 banner && Base.banner(term)
344 if term.term_type == "dumb"
345 active_repl = REPL.BasicREPL(term)
346 quiet || @warn "Terminal not fully functional"
347 else
348 active_repl = REPL.LineEditREPL(term, have_color, true)
349 active_repl.history_file = history_file
350 end
351 # Make sure any displays pushed in .julia/config/startup.jl ends up above the
352 # REPLDisplay
353 pushdisplay(REPL.REPLDisplay(active_repl))
354 _atreplinit(active_repl)
355 REPL.run_repl(active_repl, backend->(global active_repl_backend = backend))
356 end
357 else
358 # otherwise provide a simple fallback
359 if interactive && !quiet
360 @warn "REPL provider not available: using basic fallback"
361 end
362 banner && Base.banner()
363 let input = stdin
364 if isa(input, File) || isa(input, IOStream)
365 # for files, we can slurp in the whole thing at once
366 ex = parse_input_line(read(input, String))
367 if Meta.isexpr(ex, :toplevel)
368 # if we get back a list of statements, eval them sequentially
369 # as if we had parsed them sequentially
370 for stmt in ex.args
371 eval_user_input(stmt, true)
372 end
373 body = ex.args
374 else
375 eval_user_input(ex, true)
376 end
377 else
378 while isopen(input) || !eof(input)
379 if interactive
380 print("julia> ")
381 flush(stdout)
382 end
383 eval_user_input(parse_input_line(input), true)
384 end
385 end
386 end
387 end
388 nothing
389 end
390
391 baremodule MainInclude
392 include(fname::AbstractString) = Main.Base.include(Main, fname)
393 eval(x) = Core.eval(Main, x)
394 end
395
396 """
397 eval(expr)
398
399 Evaluate an expression in the global scope of the containing module.
400 Every `Module` (except those defined with `baremodule`) has its own 1-argument
401 definition of `eval`, which evaluates expressions in that module.
402 """
403 MainInclude.eval
404
405 """
406 include(path::AbstractString)
407
408 Evaluate the contents of the input source file in the global scope of the containing module.
409 Every module (except those defined with `baremodule`) has its own 1-argument
410 definition of `include`, which evaluates the file in that module.
411 Returns the result of the last evaluated expression of the input file. During including,
412 a task-local include path is set to the directory containing the file. Nested calls to
413 `include` will search relative to that path. This function is typically used to load source
414 interactively, or to combine files in packages that are broken into multiple source files.
415
416 Use [`Base.include`](@ref) to evaluate a file into another module.
417 """
418 MainInclude.include
419
420
40 (95.24%) samples spent in _start
function _start()
421 empty!(ARGS)
422 append!(ARGS, Core.ARGS)
423 @eval Main import Base.MainInclude: eval, include
424 try
425 40 (95.24%)
40 (100.00%) samples spent calling exec_options
exec_options(JLOptions())
426 catch err
427 invokelatest(display_error, err, catch_backtrace())
428 exit(1)
429 end
430 if is_interactive && have_color
431 print(color_normal)
432 end
433 end