StatProfilerHTML.jl report
Generated on Thu, 26 Mar 2020 19:09:01
File source code
Line Exclusive Inclusive Code
1 module Revise
2
3 using FileWatching, REPL, Distributed, UUIDs
4 import LibGit2
5 using Base: PkgId
6 using Base.Meta: isexpr
7 using Core: CodeInfo
8
9 using OrderedCollections, CodeTracking, JuliaInterpreter, LoweredCodeUtils
10 using CodeTracking: PkgFiles, basedir, srcfiles
11 using JuliaInterpreter: whichtt, is_doc_expr, step_expr!, finish_and_return!, get_return
12 using JuliaInterpreter: @lookup, moduleof, scopeof, pc_expr, prepare_thunk, split_expressions
13 using LoweredCodeUtils: next_or_nothing!, isanonymous_typedef, define_anonymous
14
15 export revise, includet, entr, MethodSummary
16
17 """
18 Revise.watching_files[]
19
20 Returns `true` if we watch files rather than their containing directory.
21 FreeBSD and NFS-mounted systems should watch files, otherwise we prefer to watch
22 directories.
23 """
24 const watching_files = Ref(Sys.KERNEL == :FreeBSD)
25
26 """
27 Revise.polling_files[]
28
29 Returns `true` if we should poll the filesystem for changes to the files that define
30 loaded code. It is preferable to avoid polling, instead relying on operating system
31 notifications via `FileWatching.watch_file`. However, NFS-mounted
32 filesystems (and perhaps others) do not support file-watching, so for code stored
33 on such filesystems you should turn polling on.
34
35 See the documentation for the `JULIA_REVISE_POLL` environment variable.
36 """
37 const polling_files = Ref(false)
38 function wait_changed(file)
39 try
40 polling_files[] ? poll_file(file) : watch_file(file)
41 catch err
42 if Sys.islinux() && err isa Base.IOError && err.code == -28 # ENOSPC
43 @warn """Your operating system has run out of inotify capacity.
44 Check the current value with `cat /proc/sys/fs/inotify/max_user_watches`.
45 Set it to a higher level with, e.g., `echo 65536 | sudo tee -a /proc/sys/fs/inotify/max_user_watches`.
46 This requires having administrative privileges on your machine (or talk to your sysadmin).
47 See https://github.com/timholy/Revise.jl/issues/26 for more information."""
48 end
49 rethrow(err)
50 end
51 return nothing
52 end
53
54 """
55 Revise.tracking_Main_includes[]
56
57 Returns `true` if files directly included from the REPL should be tracked.
58 The default is `false`. See the documentation regarding the `JULIA_REVISE_INCLUDE`
59 environment variable to customize it.
60 """
61 const tracking_Main_includes = Ref(false)
62
63 include("relocatable_exprs.jl")
64 include("types.jl")
65 include("utils.jl")
66 include("parsing.jl")
67 include("backedges.jl")
68 include("lowered.jl")
69 include("pkgs.jl")
70 include("git.jl")
71 include("recipes.jl")
72 include("logging.jl")
73 include("deprecations.jl")
74
75 ### Globals to keep track of state
76
77 """
78 Revise.watched_files
79
80 Global variable, `watched_files[dirname]` returns the collection of files in `dirname`
81 that we're monitoring for changes. The returned value has type [`Revise.WatchList`](@ref).
82
83 This variable allows us to watch directories rather than files, reducing the burden on
84 the OS.
85 """
86 const watched_files = Dict{String,WatchList}()
87
88 """
89 Revise.revision_queue
90
91 Global variable, `revision_queue` holds `(pkgdata,filename)` pairs that we need to revise, meaning
92 that these files have changed since we last processed a revision.
93 This list gets populated by callbacks that watch directories for updates.
94 """
95 const revision_queue = Set{Tuple{PkgData,String}}()
96
97 """
98 Revise.queue_errors
99
100 Global variable, maps `(pkgdata, filename)` pairs that errored upon last revision to
101 `(exception, backtrace)`.
102 """
103 const queue_errors = Dict{Tuple{PkgData,String},Tuple{Exception, Any}}()
104
105 """
106 Revise.pkgdatas
107
108 `pkgdatas` is the core information that tracks the relationship between source code
109 and julia objects, and allows re-evaluation of code in the proper module scope.
110 It is a dictionary indexed by PkgId:
111 `pkgdatas[id]` returns a value of type [`Revise.PkgData`](@ref).
112 """
113 const pkgdatas = Dict{PkgId,PkgData}()
114
115 const moduledeps = Dict{Module,DepDict}()
116 function get_depdict(mod::Module)
117 if !haskey(moduledeps, mod)
118 moduledeps[mod] = DepDict()
119 end
120 return moduledeps[mod]
121 end
122
123 """
124 Revise.included_files
125
126 Global variable, `included_files` gets populated by callbacks we register with `include`.
127 It's used to track non-precompiled packages and, optionally, user scripts (see docs on
128 `JULIA_REVISE_INCLUDE`).
129 """
130 const included_files = Tuple{Module,String}[] # (module, filename)
131
132 """
133 Revise.basesrccache
134
135 Full path to the running Julia's cache of source code defining `Base`.
136 """
137 const basesrccache = normpath(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "base.cache"))
138
139 """
140 Revise.basebuilddir
141
142 Julia's top-level directory when Julia was built, as recorded by the entries in
143 `Base._included_files`.
144 """
145 const basebuilddir = begin
146 sysimg = filter(x->endswith(x[2], "sysimg.jl"), Base._included_files)[1][2]
147 dirname(dirname(sysimg))
148 end
149
150 """
151 Revise.juliadir
152
153 Constant specifying full path to julia top-level source directory.
154 This should be reliable even for local builds, cross-builds, and binary installs.
155 """
156 const juliadir = begin
157 local jldir = basebuilddir
158 try
159 isdir(joinpath(jldir, "base")) || throw(ErrorException("$(jldir) does not have \"base\""))
160 catch
161 # Binaries probably end up here. We fall back on Sys.BINDIR
162 jldir = joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia")
163 if !isdir(joinpath(jldir, "base"))
164 while true
165 trydir = joinpath(jldir, "base")
166 isdir(trydir) && break
167 trydir = joinpath(jldir, "share", "julia", "base")
168 if isdir(trydir)
169 jldir = joinpath(jldir, "share", "julia")
170 break
171 end
172 jldirnext = dirname(jldir)
173 jldirnext == jldir && break
174 jldir = jldirnext
175 end
176 end
177 end
178 normpath(jldir)
179 end
180 const cache_file_key = Dict{String,String}() # corrected=>uncorrected filenames
181 const src_file_key = Dict{String,String}() # uncorrected=>corrected filenames
182
183 """
184 Revise.dont_watch_pkgs
185
186 Global variable, use `push!(Revise.dont_watch_pkgs, :MyPackage)` to prevent Revise
187 from tracking changes to `MyPackage`. You can do this from the REPL or from your
188 `.julia/config/startup.jl` file.
189
190 See also [`Revise.silence`](@ref).
191 """
192 const dont_watch_pkgs = Set{Symbol}()
193 const silence_pkgs = Set{Symbol}()
194 const depsdir = joinpath(dirname(@__DIR__), "deps")
195 const silencefile = Ref(joinpath(depsdir, "silence.txt")) # Ref so that tests don't clobber
196
197 ##
198 ## The inputs are sets of expressions found in each file.
199 ## Some of those expressions will generate methods which are identified via their signatures.
200 ## From "old" expressions we know their corresponding signatures, but from "new"
201 ## expressions we have not yet computed them. This makes old and new asymmetric.
202 ##
203 ## Strategy:
204 ## - For every old expr not found in the new ones,
205 ## + delete the corresponding methods (using the signatures we've previously computed)
206 ## + remove the sig entries from CodeTracking.method_info (")
207 ## Best to do all the deletion first (across all files and modules) in case a method is
208 ## simply being moved from one file to another.
209 ## - For every new expr found among the old ones,
210 ## + update the location info in CodeTracking.method_info
211 ## - For every new expr not found in the old ones,
212 ## + eval the expr
213 ## + extract signatures
214 ## + add to the ModuleExprsSigs
215 ## + add to CodeTracking.method_info
216 ##
217 ## Interestingly, the ex=>sigs link may not be the same as the sigs=>ex link.
218 ## Consider a conditional block,
219 ## if Sys.islinux()
220 ## f() = 1
221 ## g() = 2
222 ## else
223 ## g() = 3
224 ## end
225 ## From the standpoint of Revise's diff-and-patch functionality, we should look for
226 ## diffs in this entire block. (Really good backedge support---or a variant of `lower` that
227 ## links back to the specific expression---might change this, but for
228 ## now this is the right strategy.) From the standpoint of CodeTracking, we should
229 ## link the signature to the actual method-defining expression (either :(f() = 1) or :(g() = 2)).
230
231 function delete_missing!(exs_sigs_old::ExprsSigs, exs_sigs_new)
232 with_logger(_debug_logger) do
233 for (ex, sigs) in exs_sigs_old
234 haskey(exs_sigs_new, ex) && continue
235 # ex was deleted
236 sigs === nothing && continue
237 for sig in sigs
238 ret = Base._methods_by_ftype(sig, -1, typemax(UInt))
239 success = false
240 if !isempty(ret)
241 m = ret[end][3]::Method # the last method returned is the least-specific that matches, and thus most likely to be type-equal
242 methsig = m.sig
243 if sig <: methsig && methsig <: sig
244 @debug "DeleteMethod" _group="Action" time=time() deltainfo=(sig, MethodSummary(m))
245 # Delete the corresponding methods
246 for p in workers()
247 try # guard against serialization errors if the type isn't defined on the worker
248 remotecall(Core.eval, p, Main, :(delete_method_by_sig($sig)))
249 catch
250 end
251 end
252 Base.delete_method(m)
253 # Remove the entries from CodeTracking data
254 delete!(CodeTracking.method_info, sig)
255 # Remove frame from JuliaInterpreter, if applicable. Otherwise debuggers
256 # may erroneously work with outdated code (265-like problems)
257 if haskey(JuliaInterpreter.framedict, m)
258 delete!(JuliaInterpreter.framedict, m)
259 end
260 if isdefined(m, :generator)
261 # defensively delete all generated functions
262 empty!(JuliaInterpreter.genframedict)
263 end
264 success = true
265 end
266 end
267 if !success
268 @debug "FailedDeletion" _group="Action" time=time() deltainfo=(sig,)
269 end
270 end
271 end
272 end
273 return exs_sigs_old
274 end
275
276 const empty_exs_sigs = ExprsSigs()
277 function delete_missing!(mod_exs_sigs_old::ModuleExprsSigs, mod_exs_sigs_new)
278 for (mod, exs_sigs_old) in mod_exs_sigs_old
279 exs_sigs_new = get(mod_exs_sigs_new, mod, empty_exs_sigs)
280 delete_missing!(exs_sigs_old, exs_sigs_new)
281 end
282 return mod_exs_sigs_old
283 end
284
285 function eval_new!(exs_sigs_new::ExprsSigs, exs_sigs_old, mod::Module)
286 includes = Vector{String}()
287 with_logger(_debug_logger) do
288 for rex in keys(exs_sigs_new)
289 rexo = getkey(exs_sigs_old, rex, nothing)
290 # extract the signatures and update the line info
291 local sigs
292 if rexo === nothing
293 ex = rex.ex
294 # ex is not present in old
295 @debug "Eval" _group="Action" time=time() deltainfo=(mod, ex)
296 # try
297 sigs, deps, _includes, thunk = eval_with_signatures(mod, ex) # All signatures defined by `ex`
298 append!(includes, _includes)
299 if VERSION < v"1.3.0" || !isexpr(thunk, :thunk)
300 thunk = ex
301 end
302 for p in workers()
303 p == myid() && continue
304 try # don't error if `mod` isn't defined on the worker
305 remotecall(Core.eval, p, mod, thunk)
306 catch
307 end
308 end
309 storedeps(deps, rex, mod)
310 # catch err
311 # @error "failure to evaluate changes in $mod"
312 # showerror(stderr, err)
313 # println_maxsize(stderr, "\n", ex; maxlines=20)
314 # end
315 else
316 sigs = exs_sigs_old[rexo]
317 # Update location info
318 ln, lno = firstline(unwrap(rex)), firstline(unwrap(rexo))
319 if sigs !== nothing && !isempty(sigs) && ln != lno
320 @debug "LineOffset" _group="Action" time=time() deltainfo=(sigs, lno=>ln)
321 for sig in sigs
322 local methloc, methdef
323 # try
324 methloc, methdef = CodeTracking.method_info[sig]
325 # catch err
326 # @show sig sigs
327 # @show CodeTracking.method_info
328 # rethrow(err)
329 # end
330 CodeTracking.method_info[sig] = (newloc(methloc, ln, lno), methdef)
331 end
332 end
333 end
334 # @show rex rexo sigs
335 exs_sigs_new[rex] = sigs
336 end
337 end
338 return exs_sigs_new, includes
339 end
340
341 function eval_new!(mod_exs_sigs_new::ModuleExprsSigs, mod_exs_sigs_old)
342 includes = Vector{Pair{Module,Vector{String}}}()
343 for (mod, exs_sigs_new) in mod_exs_sigs_new
344 exs_sigs_old = get(mod_exs_sigs_old, mod, empty_exs_sigs)
345 _, _includes = eval_new!(exs_sigs_new, exs_sigs_old, mod)
346 push!(includes, mod=>_includes)
347 end
348 return mod_exs_sigs_new, includes
349 end
350
351 struct CodeTrackingMethodInfo
352 exprstack::Vector{Expr}
353 allsigs::Vector{Any}
354 deps::Set{Union{GlobalRef,Symbol}}
355 includes::Vector{String}
356 end
357 CodeTrackingMethodInfo(ex::Expr) = CodeTrackingMethodInfo([ex], Any[], Set{Union{GlobalRef,Symbol}}(), String[])
358 CodeTrackingMethodInfo(rex::RelocatableExpr) = CodeTrackingMethodInfo(rex.ex)
359
360 function add_signature!(methodinfo::CodeTrackingMethodInfo, @nospecialize(sig), ln)
361 CodeTracking.method_info[sig] = (fixpath(ln), methodinfo.exprstack[end])
362 push!(methodinfo.allsigs, sig)
363 return methodinfo
364 end
365 push_expr!(methodinfo::CodeTrackingMethodInfo, mod::Module, ex::Expr) = (push!(methodinfo.exprstack, ex); methodinfo)
366 pop_expr!(methodinfo::CodeTrackingMethodInfo) = (pop!(methodinfo.exprstack); methodinfo)
367 function add_dependencies!(methodinfo::CodeTrackingMethodInfo, be::BackEdges, src, chunks)
368 isempty(src.code) && return methodinfo
369 stmt1 = first(src.code)
370 if isexpr(stmt1, :gotoifnot) && isa(stmt1.args[1], Union{GlobalRef,Symbol})
371 if any(chunk->hastrackedexpr(src, chunk), chunks)
372 push!(methodinfo.deps, stmt1.args[1])
373 end
374 end
375 # for (dep, lines) in be.byname
376 # for ln in lines
377 # stmt = src.code[ln]
378 # if isexpr(stmt, :(=)) && stmt.args[1] == dep
379 # continue
380 # else
381 # push!(methodinfo.deps, dep)
382 # end
383 # end
384 # end
385 return methodinfo
386 end
387 function add_includes!(methodinfo::CodeTrackingMethodInfo, filename)
388 push!(methodinfo.includes, filename)
389 return methodinfo
390 end
391
392 # Eval and insert into CodeTracking data
393 function eval_with_signatures(mod, ex::Expr; define=true, kwargs...)
394 methodinfo = CodeTrackingMethodInfo(ex)
395 docexprs = Dict{Module,Vector{Expr}}()
396 _, frame = methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; define=define, kwargs...)
397 return methodinfo.allsigs, methodinfo.deps, methodinfo.includes, frame
398 end
399
400 function instantiate_sigs!(modexsigs::ModuleExprsSigs; define=false, kwargs...)
401 for (mod, exsigs) in modexsigs
402 for rex in keys(exsigs)
403 is_doc_expr(rex.ex) && continue
404 sigs, deps, _ = eval_with_signatures(mod, rex.ex; define=define, kwargs...)
405 exsigs[rex.ex] = sigs
406 storedeps(deps, rex, mod)
407 end
408 end
409 return modexsigs
410 end
411
412 function storedeps(deps, rex, mod)
413 for dep in deps
414 if isa(dep, GlobalRef)
415 haskey(moduledeps, dep.mod) || continue
416 ddict, sym = get_depdict(dep.mod), dep.name
417 else
418 ddict, sym = get_depdict(mod), dep
419 end
420 if !haskey(ddict, sym)
421 ddict[sym] = Set{DepDictVals}()
422 end
423 push!(ddict[sym], (mod, rex))
424 end
425 return rex
426 end
427
428 # This is intended for testing purposes, but not general use. The key problem is
429 # that it doesn't properly handle methods that move from one file to another; there is the
430 # risk you could end up deleting the method altogether depending on the order in which you
431 # process these.
432 # See `revise` for the proper approach.
433 function eval_revised(mod_exs_sigs_new, mod_exs_sigs_old)
434 delete_missing!(mod_exs_sigs_old, mod_exs_sigs_new)
435 eval_new!(mod_exs_sigs_new, mod_exs_sigs_old) # note: drops `includes`
436 instantiate_sigs!(mod_exs_sigs_new)
437 end
438
439 """
440 Revise.init_watching(files)
441 Revise.init_watching(pkgdata::PkgData, files)
442
443 For every filename in `files`, monitor the filesystem for updates. When the file is
444 updated, either [`Revise.revise_dir_queued`](@ref) or [`Revise.revise_file_queued`](@ref) will
445 be called.
446
447 Use the `pkgdata` version if the files are supplied using relative paths.
448 """
449 function init_watching(pkgdata::PkgData, files)
450 udirs = Set{String}()
451 for file in files
452 dir, basename = splitdir(file)
453 dirfull = joinpath(basedir(pkgdata), dir)
454 already_watching = haskey(watched_files, dirfull)
455 already_watching || (watched_files[dirfull] = WatchList())
456 push!(watched_files[dirfull], basename=>pkgdata)
457 if watching_files[]
458 fwatcher = Rescheduler(revise_file_queued, (pkgdata, file))
459 schedule(Task(fwatcher))
460 else
461 already_watching || push!(udirs, dir)
462 end
463 end
464 for dir in udirs
465 dirfull = joinpath(basedir(pkgdata), dir)
466 updatetime!(watched_files[dirfull])
467 if !watching_files[]
468 dwatcher = Rescheduler(revise_dir_queued, (dirfull,))
469 schedule(Task(dwatcher))
470 end
471 end
472 return nothing
473 end
474 init_watching(files) = init_watching(PkgId(Main), files)
475
476 """
477 revise_dir_queued(dirname)
478
479 Wait for one or more of the files registered in `Revise.watched_files[dirname]` to be
480 modified, and then queue the corresponding files on [`Revise.revision_queue`](@ref).
481 This is generally called via a [`Revise.Rescheduler`](@ref).
482 """
483 @noinline function revise_dir_queued(dirname)
484 @assert isabspath(dirname)
485 if !isdir(dirname)
486 sleep(0.1) # in case git has done a delete/replace cycle
487 if !isdir(dirname)
488 with_logger(SimpleLogger(stderr)) do
489 @warn "$dirname is not an existing directory, Revise is not watching"
490 end
491 return false
492 end
493 end
494 latestfiles, stillwatching = watch_files_via_dir(dirname) # will block here until file(s) change
495 for (file, id) in latestfiles
496 key = joinpath(dirname, file)
497 pkgdata = pkgdatas[id]
498 if hasfile(pkgdata, key) # issue #228
499 push!(revision_queue, (pkgdata, relpath(key, pkgdata)))
500 end
501 end
502 return stillwatching
503 end
504
505 # See #66.
506 """
507 revise_file_queued(pkgdata::PkgData, filename)
508
509 Wait for modifications to `filename`, and then queue the corresponding files on [`Revise.revision_queue`](@ref).
510 This is generally called via a [`Revise.Rescheduler`](@ref).
511
512 This is used only on platforms (like BSD) which cannot use [`Revise.revise_dir_queued`](@ref).
513 """
514 function revise_file_queued(pkgdata::PkgData, file)
515 file0 = file
516 if !isabspath(file)
517 file = joinpath(basedir(pkgdata), file)
518 end
519 if !file_exists(file)
520 sleep(0.1) # in case git has done a delete/replace cycle
521 if !file_exists(file)
522 push!(revision_queue, (pkgdata, file0)) # process file deletions
523 return false
524 end
525 end
526
527 wait_changed(file) # will block here until the file changes
528 # Check to see if we're still watching this file
529 dirfull, basename = splitdir(file)
530 if haskey(watched_files, dirfull)
531 push!(revision_queue, (pkgdata, file0))
532 return true
533 end
534 return false
535 end
536
537 # Because we delete first, we have to make sure we've parsed the file
538 function handle_deletions(pkgdata, file)
539 fi = maybe_parse_from_cache!(pkgdata, file)
540 mexsold = fi.modexsigs
541 filep = normpath(joinpath(basedir(pkgdata), file))
542 topmod = first(keys(mexsold))
543 mexsnew = file_exists(filep) ? parse_source(filep, topmod) :
544 (@warn("$filep no longer exists, deleting all methods"); ModuleExprsSigs(topmod))
545 if mexsnew !== nothing
546 delete_missing!(mexsold, mexsnew)
547 end
548 return mexsnew, mexsold
549 end
550
551 """
552 Revise.revise_file_now(pkgdata::PkgData, file)
553
554 Process revisions to `file`. This parses `file` and computes an expression-level diff
555 between the current state of the file and its most recently evaluated state.
556 It then deletes any removed methods and re-evaluates any changed expressions.
557 Note that generally it is better to use [`revise`](@ref) as it properly handles methods
558 that move from one file to another.
559
560 `id` must be a key in [`Revise.pkgdatas`](@ref), and `file` a key in
561 `Revise.pkgdatas[id].fileinfos`.
562 """
563 function revise_file_now(pkgdata::PkgData, file)
564 i = fileindex(pkgdata, file)
565 if i === nothing
566 println("Revise is currently tracking the following files in $(pkgdata.id): ", keys(pkgdict))
567 error(file, " is not currently being tracked.")
568 end
569 mexsnew, mexsold = handle_deletions(pkgdata, file)
570 if mexsnew != nothing
571 _, includes = eval_new!(mexsnew, mexsold)
572 fi = fileinfo(pkgdata, i)
573 pkgdata.fileinfos[i] = FileInfo(mexsnew, fi)
574 maybe_add_includes_to_pkgdata!(pkgdata, file, includes)
575 end
576 nothing
577 end
578
579 """
580 Revise.errors()
581
582 Report the errors represented in [`Revise.queue_errors`](@ref).
583 Errors are automatically reported the first time they are encountered, but this function
584 can be used to report errors again.
585 """
586 function errors(revision_errors=keys(queue_errors))
587 for (pkgdata, file) in revision_errors
588 (err, bt) = queue_errors[(pkgdata, file)]
589 fullpath = joinpath(basedir(pkgdata), file)
590 @error "Failed to revise $fullpath" exception=(err, trim_toplevel!(bt))
591 end
592 end
593
594 """
595 revise()
596
597 `eval` any changes in the revision queue. See [`Revise.revision_queue`](@ref).
598 """
599 function revise()
600 sleep(0.01) # in case the file system isn't quite done writing out the new files
601
602 # Do all the deletion first. This ensures that a method that moved from one file to another
603 # won't get redefined first and deleted second.
604 revision_errors = []
605 finished = eltype(revision_queue)[]
606 mexsnews = ModuleExprsSigs[]
607 interrupt = false
608 for (pkgdata, file) in revision_queue
609 try
610 push!(mexsnews, handle_deletions(pkgdata, file)[1])
611 push!(finished, (pkgdata, file))
612 catch err
613 interrupt |= isa(err, InterruptException)
614 push!(revision_errors, (pkgdata, file))
615 queue_errors[(pkgdata, file)] = (err, catch_backtrace())
616 end
617 end
618 # Do the evaluation
619 for ((pkgdata, file), mexsnew) in zip(finished, mexsnews)
620 i = fileindex(pkgdata, file)
621 fi = fileinfo(pkgdata, i)
622 try
623 _, includes = eval_new!(mexsnew, fi.modexsigs)
624 pkgdata.fileinfos[i] = FileInfo(mexsnew, fi)
625 delete!(queue_errors, (pkgdata, file))
626 maybe_add_includes_to_pkgdata!(pkgdata, file, includes)
627 catch err
628 interrupt |= isa(err, InterruptException)
629 push!(revision_errors, (pkgdata, file))
630 queue_errors[(pkgdata, file)] = (err, catch_backtrace())
631 end
632 end
633 if interrupt
634 for pkgfile in finished
635 haskey(queue_errors, pkgfile) || delete!(revision_queue, pkgfile)
636 end
637 else
638 empty!(revision_queue)
639 end
640 errors(revision_errors)
641 if !isempty(queue_errors)
642 io = IOBuffer()
643 println(io, "\n") # better here than in the triple-quoted literal, see https://github.com/JuliaLang/julia/issues/34105
644 for (pkgdata, file) in keys(queue_errors)
645 println(io, " ", joinpath(basedir(pkgdata), file))
646 end
647 str = String(take!(io))
648 @warn """Due to a previously reported error, the running code does not match saved version for the following files:$str
649 Use Revise.errors() to report errors again."""
650 end
651 tracking_Main_includes[] && queue_includes(Main)
652 nothing
653 end
654 revise(backend::REPL.REPLBackend) = revise()
655
656 """
657 revise(mod::Module)
658
659 Reevaluate every definition in `mod`, whether it was changed or not. This is useful
660 to propagate an updated macro definition, or to force recompiling generated functions.
661 """
662 function revise(mod::Module)
663 mod == Main && error("cannot revise(Main)")
664 id = PkgId(mod)
665 pkgdata = pkgdatas[id]
666 for (i, file) in enumerate(srcfiles(pkgdata))
667 fi = fileinfo(pkgdata, i)
668 for (mod, exsigs) in fi.modexsigs
669 for def in keys(exsigs)
670 ex = def.ex
671 exuw = unwrap(ex)
672 isexpr(exuw, :call) && exuw.args[1] == :include && continue
673 try
674 Core.eval(mod, ex)
675 catch err
676 @show mod
677 display(ex)
678 rethrow(err)
679 end
680 end
681 end
682 end
683 return true # fixme try/catch?
684 end
685
686 """
687 Revise.track(mod::Module, file::AbstractString)
688 Revise.track(file::AbstractString)
689
690 Watch `file` for updates and [`revise`](@ref) loaded code with any
691 changes. `mod` is the module into which `file` is evaluated; if omitted,
692 it defaults to `Main`.
693
694 If this produces many errors, check that you specified `mod` correctly.
695 """
696 function track(mod::Module, file::AbstractString; kwargs...)
697 isfile(file) || error(file, " is not a file")
698 # Determine whether we're already tracking this file
699 id = PkgId(mod)
700 file = normpath(abspath(file))
701 haskey(pkgdatas, id) && hasfile(pkgdatas[id], file) && return nothing
702 # Set up tracking
703 fm = parse_source(file, mod)
704 if fm !== nothing
705 instantiate_sigs!(fm; kwargs...)
706 if !haskey(pkgdatas, id)
707 # Wait a bit to see if `mod` gets initialized
708 # This can happen if the module's __init__ function
709 # calls `track`, e.g., via a @require. Ref issue #403.
710 sleep(0.1)
711 end
712 if !haskey(pkgdatas, id)
713 pkgdatas[id] = PkgData(id, pathof(mod))
714 end
715 pkgdata = pkgdatas[id]
716 if !haskey(CodeTracking._pkgfiles, id)
717 CodeTracking._pkgfiles[id] = pkgdata.info
718 end
719 push!(pkgdata, relpath(file, pkgdata)=>FileInfo(fm))
720 init_watching(pkgdata, (file,))
721 end
722 return nothing
723 end
724
725 function track(file::AbstractString; kwargs...)
726 startswith(file, juliadir) && error("use Revise.track(Base) or Revise.track(<stdlib module>)")
727 track(Main, file; kwargs...)
728 end
729
730 """
731 includet(filename)
732
733 Load `filename` and track any future changes to it. `includet` is essentially shorthand for
734
735 Revise.track(Main, filename; define=true, skip_include=false)
736
737 `includet` is intended for "user scripts," e.g., a file you use locally for a specific
738 purpose such as loading a specific data set or performing a particular analysis.
739 Do *not* use `includet` for packages, as those should be handled by `using` or `import`.
740 (If you're working with code in Base or one of Julia's standard libraries, use
741 `Revise.track(mod)` instead, where `mod` is the module.)
742 If `using` and `import` aren't working, you may have packages in a non-standard location;
743 try fixing it with something like `push!(LOAD_PATH, "/path/to/my/private/repos")`.
744
745 `includet` is deliberately non-recursive, so if `filename` loads any other files,
746 they will not be automatically tracked.
747 (See [`Revise.track`](@ref) to set it up manually.)
748 """
749 function includet(mod::Module, file::AbstractString)
750 prev = Base.source_path(nothing)
751 if prev === nothing
752 file = abspath(file)
753 else
754 file = normpath(joinpath(dirname(prev), file))
755 end
756 tls = task_local_storage()
757 tls[:SOURCE_PATH] = file
758 try
759 track(mod, file; define=true, skip_include=false)
760 finally
761 if prev === nothing
762 delete!(tls, :SOURCE_PATH)
763 else
764 tls[:SOURCE_PATH] = prev
765 end
766 end
767 return nothing
768 end
769 includet(file::AbstractString) = includet(Main, file)
770
771 """
772 entr(f, files; postpone=false, pause=0.02)
773 entr(f, files, modules; postpone=false, pause=0.02)
774
775 Execute `f()` whenever files listed in `files`, or code in `modules`, updates.
776 `entr` will process updates (and block your command line) until you press Ctrl-C.
777 Unless `postpone` is `true`, `f()` will be executed also when calling `entr`,
778 regardless of file changes. The `pause` is the period (in seconds) that `entr`
779 will wait between being triggered and actually calling `f()`, to handle
780 clusters of modifications, such as those produced by saving files in certain
781 text editors.
782
783 # Example
784
785 ```julia
786 entr(["/tmp/watched.txt"], [Pkg1, Pkg2]) do
787 println("update")
788 end
789 ```
790 This will print "update" every time `"/tmp/watched.txt"` or any of the code defining
791 `Pkg1` or `Pkg2` gets updated.
792 """
793 function entr(f::Function, files, modules=nothing; postpone=false, pause=0.02)
794 yield()
795 files = collect(files) # because we may add to this list
796 if modules !== nothing
797 for mod in modules
798 id = PkgId(mod)
799 pkgdata = pkgdatas[id]
800 for file in srcfiles(pkgdata)
801 push!(files, joinpath(basedir(pkgdata), file))
802 end
803 end
804 end
805 active = true
806 try
807 @sync begin
808 postpone || f()
809 for file in files
810 waitfor = isdir(file) ? watch_folder : watch_file
811 @async while active
812 ret = waitfor(file, 1)
813 if active && (ret.changed || ret.renamed)
814 sleep(pause)
815 revise()
816 Base.invokelatest(f)
817 end
818 end
819 end
820 end
821 catch err
822 if isa(err, InterruptException)
823 active = false
824 else
825 rethrow(err)
826 end
827 end
828 end
829
830 """
831 Revise.silence(pkg)
832
833 Silence warnings about not tracking changes to package `pkg`.
834 """
835 function silence(pkg::Symbol)
836 push!(silence_pkgs, pkg)
837 if !isdir(depsdir)
838 mkpath(depsdir)
839 end
840 open(silencefile[], "w") do io
841 for p in silence_pkgs
842 println(io, p)
843 end
844 end
845 nothing
846 end
847 silence(pkg::AbstractString) = silence(Symbol(pkg))
848
849 ## Utilities
850
851 """
852 method = get_method(sigt)
853
854 Get the method `method` with signature-type `sigt`. This is used to provide
855 the method to `Base.delete_method`.
856
857 If `sigt` does not correspond to a method, returns `nothing`.
858
859 # Examples
860
861 ```jldoctest; setup = :(using Revise), filter = r"in Main at.*"
862 julia> mymethod(::Int) = 1
863 mymethod (generic function with 1 method)
864
865 julia> mymethod(::AbstractFloat) = 2
866 mymethod (generic function with 2 methods)
867
868 julia> Revise.get_method(Tuple{typeof(mymethod), Int})
869 mymethod(::Int64) in Main at REPL[0]:1
870
871 julia> Revise.get_method(Tuple{typeof(mymethod), Float64})
872 mymethod(::AbstractFloat) in Main at REPL[1]:1
873
874 julia> Revise.get_method(Tuple{typeof(mymethod), Number})
875
876 ```
877 """
878 function get_method(@nospecialize(sigt))
879 mths = Base._methods_by_ftype(sigt, -1, typemax(UInt))
880 length(mths) == 1 && return mths[1][3]
881 if !isempty(mths)
882 # There might be many methods, but the one that should match should be the
883 # last one, since methods are ordered by specificity
884 i = lastindex(mths)
885 while i > 0
886 m = mths[i][3]
887 m.sig == sigt && return m
888 i -= 1
889 end
890 end
891 return nothing
892 end
893
894 """
895 success = get_def(method::Method)
896
897 As needed, load the source file necessary for extracting the code defining `method`.
898 The source-file defining `method` must be tracked.
899 If it is in Base, this will execute `track(Base)` if necessary.
900
901 This is a callback function used by `CodeTracking.jl`'s `definition`.
902 """
903 function get_def(method::Method; modified_files=revision_queue)
904 yield() # magic bug fix for the OSX test failures. TODO: figure out why this works (prob. Julia bug)
905 if method.file == :none && String(method.name)[1] == '#'
906 # This is likely to be a kwarg method, try to find something with location info
907 method = bodymethod(method)
908 end
909 filename = fixpath(String(method.file))
910 if startswith(filename, "REPL[")
911 isdefined(Base, :active_repl) || return false
912 fi = add_definitions_from_repl(filename)
913 hassig = false
914 for (mod, exs) in fi.modexsigs
915 for sigs in values(exs)
916 hassig |= !isempty(sigs)
917 end
918 end
919 return hassig
920 end
921 id = get_tracked_id(method.module; modified_files=modified_files)
922 id === nothing && return false
923 pkgdata = pkgdatas[id]
924 filename = relpath(filename, pkgdata)
925 if hasfile(pkgdata, filename)
926 def = get_def(method, pkgdata, filename)
927 def !== nothing && return true
928 end
929 # Lookup can fail for macro-defined methods, see https://github.com/JuliaLang/julia/issues/31197
930 # We need to find the right file.
931 if method.module == Base || method.module == Core || method.module == Core.Compiler
932 @warn "skipping $method to avoid parsing too much code"
933 CodeTracking.method_info[method.sig] = missing
934 return false
935 end
936 parentfile, included_files = modulefiles(method.module)
937 if parentfile !== nothing
938 def = get_def(method, pkgdata, relpath(parentfile, pkgdata))
939 def !== nothing && return true
940 for modulefile in included_files
941 def = get_def(method, pkgdata, relpath(modulefile, pkgdata))
942 def !== nothing && return true
943 end
944 end
945 # As a last resort, try every file in the package
946 for file in srcfiles(pkgdata)
947 def = get_def(method, pkgdata, file)
948 def !== nothing && return true
949 end
950 @warn "$(method.sig) was not found"
951 # So that we don't call it again, store missingness info in CodeTracking
952 CodeTracking.method_info[method.sig] = missing
953 return false
954 end
955
956 function get_def(method, pkgdata, filename)
957 maybe_parse_from_cache!(pkgdata, filename)
958 return get(CodeTracking.method_info, method.sig, nothing)
959 end
960
961 function get_tracked_id(id::PkgId; modified_files=revision_queue)
962 # Methods from Base or the stdlibs may require that we start tracking
963 if !haskey(pkgdatas, id)
964 recipe = id.name === "Compiler" ? :Compiler : Symbol(id.name)
965 recipe == :Core && return nothing
966 _track(id, recipe; modified_files=modified_files)
967 @info "tracking $recipe"
968 if !haskey(pkgdatas, id)
969 @warn "despite tracking $recipe, $id was not found"
970 return nothing
971 end
972 end
973 return id
974 end
975 get_tracked_id(mod::Module; modified_files=revision_queue) =
976 get_tracked_id(PkgId(mod); modified_files=modified_files)
977
978 function get_expressions(id::PkgId, filename)
979 get_tracked_id(id)
980 pkgdata = pkgdatas[id]
981 maybe_parse_from_cache!(pkgdata, filename)
982 fi = fileinfo(pkgdata, filename)
983 return fi.modexsigs
984 end
985
986 function add_definitions_from_repl(filename)
987 hist_idx = parse(Int, filename[6:end-1])
988 hp = Base.active_repl.interface.modes[1].hist
989 src = hp.history[hp.start_idx+hist_idx]
990 id = PkgId(nothing, "@REPL")
991 pkgdata = pkgdatas[id]
992 mexs = ModuleExprsSigs(Main)
993 parse_source!(mexs, src, filename, Main)
994 instantiate_sigs!(mexs)
995 fi = FileInfo(mexs)
996 push!(pkgdata, filename=>fi)
997 return fi
998 end
999
1000 function fix_line_statements!(ex::Expr, file::Symbol, line_offset::Int=0)
1001 if ex.head == :line
1002 ex.args[1] += line_offset
1003 ex.args[2] = file
1004 else
1005 for (i, a) in enumerate(ex.args)
1006 if isa(a, Expr)
1007 fix_line_statements!(a::Expr, file, line_offset)
1008 elseif isa(a, LineNumberNode)
1009 ex.args[i] = file_line_statement(a::LineNumberNode, file, line_offset)
1010 end
1011 end
1012 end
1013 ex
1014 end
1015
1016 file_line_statement(lnn::LineNumberNode, file::Symbol, line_offset) =
1017 LineNumberNode(lnn.line + line_offset, file)
1018
1019 function update_stacktrace_lineno!(trace)
1020 local nrep
1021 for i = 1:length(trace)
1022 t = trace[i]
1023 has_nrep = !isa(t, StackTraces.StackFrame)
1024 if has_nrep
1025 t, nrep = t
1026 end
1027 t = t::StackTraces.StackFrame
1028 if t.linfo isa Core.MethodInstance
1029 m = t.linfo.def
1030 sigt = m.sig
1031 # Why not just call `whereis`? Because that forces tracking. This is being
1032 # clever by recognizing that these entries exist only if there have been updates.
1033 updated = get(CodeTracking.method_info, sigt, nothing)
1034 if updated !== nothing
1035 lnn = updated[1]
1036 lineoffset = lnn.line - m.line
1037 t = StackTraces.StackFrame(t.func, lnn.file, t.line+lineoffset, t.linfo, t.from_c, t.inlined, t.pointer)
1038 trace[i] = has_nrep ? (t, nrep) : t
1039 end
1040 end
1041 end
1042 return trace
1043 end
1044
1045 function method_location(method::Method)
1046 # Why not just call `whereis`? Because that forces tracking. This is being
1047 # clever by recognizing that these entries exist only if there have been updates.
1048 updated = get(CodeTracking.method_info, method.sig, nothing)
1049 if updated !== nothing
1050 lnn = updated[1]
1051 return lnn.file, lnn.line
1052 end
1053 return method.file, method.line
1054 end
1055
1056
833 (100 %) samples spent in run_backend
833 (100 %) (incl.) when called from #85 line 358
@noinline function run_backend(backend)
1057 while true
1058 tls = task_local_storage()
1059 tls[:SOURCE_PATH] = nothing
1060 ast, show_value = take!(backend.repl_channel)
1061 if show_value == -1
1062 # exit flag
1063 break
1064 end
1065 # Process revisions, skipping `exit()` (issue #327)
1066 if !isa(ast, Expr) || length(ast.args) < 2 || (ex = ast.args[2]; !isexpr(ex, :call)) || length(ex.args) != 1 || ex.args[1] != :exit
1067 revise(backend)
1068 end
1069 # Now eval the input
1070 833 (100 %)
833 (100 %) samples spent calling eval_user_input
REPL.eval_user_input(ast, backend)
1071 end
1072 nothing
1073 end
1074
1075 """
1076 steal_repl_backend(backend = Base.active_repl_backend)
1077
1078 Replace the REPL's normal backend with one that calls [`revise`](@ref) before executing
1079 any REPL input.
1080 """
1081 function steal_repl_backend(backend = Base.active_repl_backend)
1082 @async begin
1083 # terminate the current backend
1084 put!(backend.repl_channel, (nothing, -1))
1085 fetch(backend.backend_task)
1086 # restart a new backend that differs only by processing the
1087 # revision queue before evaluating each user input
1088 backend.backend_task = @async run_backend(backend)
1089 end
1090 nothing
1091 end
1092
1093 function wait_steal_repl_backend()
1094 iter = 0
1095 # wait for active_repl_backend to exist
1096 while !isdefined(Base, :active_repl_backend) && iter < 20
1097 sleep(0.05)
1098 iter += 1
1099 end
1100 if isdefined(Base, :active_repl_backend)
1101 steal_repl_backend(Base.active_repl_backend)
1102 else
1103 @warn "REPL initialization failed, Revise is not in automatic mode. Call `revise()` manually."
1104 end
1105 end
1106
1107 """
1108 Revise.async_steal_repl_backend()
1109
1110 Wait for the REPL to complete its initialization, and then call [`Revise.steal_repl_backend`](@ref).
1111 This is necessary because code registered with `atreplinit` runs before the REPL is
1112 initialized, and there is no corresponding way to register code to run after it is complete.
1113 """
1114 function async_steal_repl_backend()
1115 mode = get(ENV, "JULIA_REVISE", "auto")
1116 if mode == "auto"
1117 atreplinit() do repl
1118 @async wait_steal_repl_backend()
1119 end
1120 end
1121 return nothing
1122 end
1123
1124 """
1125 Revise.init_worker(p)
1126
1127 Define methods on worker `p` that Revise needs in order to perform revisions on `p`.
1128 Revise itself does not need to be running on `p`.
1129 """
1130 function init_worker(p)
1131 remotecall(Core.eval, p, Main, quote
1132 function whichtt(sig)
1133 ret = Base._methods_by_ftype(sig, -1, typemax(UInt))
1134 isempty(ret) && return nothing
1135 m = ret[end][3]::Method # the last method returned is the least-specific that matches, and thus most likely to be type-equal
1136 methsig = m.sig
1137 (sig <: methsig && methsig <: sig) || return nothing
1138 return m
1139 end
1140 function delete_method_by_sig(sig)
1141 m = whichtt(sig)
1142 isa(m, Method) && Base.delete_method(m)
1143 end
1144 end)
1145 end
1146
1147 function __init__()
1148 myid() == 1 || return nothing
1149 if isfile(silencefile[])
1150 pkgs = readlines(silencefile[])
1151 for pkg in pkgs
1152 push!(silence_pkgs, Symbol(pkg))
1153 end
1154 end
1155 push!(Base.package_callbacks, watch_package)
1156 push!(Base.include_callbacks,
1157 (mod::Module, fn::AbstractString) -> push!(included_files, (mod, normpath(abspath(fn)))))
1158 mode = get(ENV, "JULIA_REVISE", "auto")
1159 if mode == "auto"
1160 if isdefined(Base, :active_repl_backend)
1161 steal_repl_backend(Base.active_repl_backend::REPL.REPLBackend)
1162 elseif isdefined(Main, :IJulia)
1163 Main.IJulia.push_preexecute_hook(revise)
1164 end
1165 if isdefined(Main, :Atom)
1166 setup_atom(getfield(Main, :Atom)::Module)
1167 end
1168 end
1169 polling = get(ENV, "JULIA_REVISE_POLL", "0")
1170 if polling == "1"
1171 polling_files[] = watching_files[] = true
1172 end
1173 rev_include = get(ENV, "JULIA_REVISE_INCLUDE", "0")
1174 if rev_include == "1"
1175 tracking_Main_includes[] = true
1176 end
1177 # Correct line numbers for code moving around
1178 Base.update_stackframes_callback[] = update_stacktrace_lineno!
1179 if isdefined(Base, :methodloc_callback)
1180 Base.methodloc_callback[] = method_location
1181 end
1182 # Populate CodeTracking data for dependencies and initialize watching
1183 for mod in (CodeTracking, OrderedCollections, JuliaInterpreter, LoweredCodeUtils)
1184 id = PkgId(mod)
1185 parse_pkg_files(id)
1186 pkgdata = pkgdatas[id]
1187 init_watching(pkgdata, srcfiles(pkgdata))
1188 end
1189 # Add `includet` to the compiled_modules (fixes #302)
1190 for m in methods(includet)
1191 push!(JuliaInterpreter.compiled_methods, m)
1192 end
1193 # Set up a repository for methods defined at the REPL
1194 id = PkgId(nothing, "@REPL")
1195 pkgdatas[id] = pkgdata = PkgData(id, nothing)
1196 # Set the lookup callbacks
1197 CodeTracking.method_lookup_callback[] = get_def
1198 CodeTracking.expressions_callback[] = get_expressions
1199
1200 # Watch the manifest file for changes
1201 mfile = manifest_file()
1202 if mfile === nothing
1203 @warn "no Manifest.toml file found, static paths used"
1204 else
1205 wmthunk = Rescheduler(watch_manifest, (mfile,))
1206 schedule(Task(wmthunk))
1207 end
1208 return nothing
1209 end
1210
1211 function setup_atom(atommod::Module)::Nothing
1212 handlers = getfield(atommod, :handlers)
1213 for x in ["eval", "evalall", "evalshow", "evalrepl"]
1214 if haskey(handlers, x)
1215 old = handlers[x]
1216 Main.Atom.handle(x) do data
1217 revise()
1218 old(data)
1219 end
1220 end
1221 end
1222 return nothing
1223 end
1224
1225 include("precompile.jl")
1226 _precompile_()
1227
1228 end # module