diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e30b022..4ea7f29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: julia -e ' using Pkg Pkg.develop(path=".") - Pkg.add(url="https://github.com/timholy/Revise.jl") + Pkg.add("Revise") Pkg.test("Revise") ' - name: Test while running Revise @@ -49,8 +49,13 @@ jobs: TERM="xterm" julia --project -i --code-coverage -e ' using InteractiveUtils, REPL, Revise, Pkg Pkg.add("ColorTypes") - @async(Base.run_main_repl(true, true, false, true, false)) - sleep(2) + t = @async( + VERSION >= v"1.12.0-DEV.612" ? Base.run_main_repl(true, true, :no, true) : + VERSION >= v"1.11.0-DEV.222" ? Base.run_main_repl(true, true, :no, true, false) : + Base.run_main_repl(true, true, false, true, false)) + isdefined(Base, :errormonitor) && Base.errormonitor(t) + while (!isdefined(Base, :active_repl_backend) || isnothing(Base.active_repl_backend)) sleep(0.1) end + pushfirst!(Base.active_repl_backend.ast_transforms, Revise.revise_first) cd("test") include("runtests.jl") if Base.VERSION.major == 1 && Base.VERSION.minor >= 9 diff --git a/src/CodeTracking.jl b/src/CodeTracking.jl index 8172f7a..585c2ec 100644 --- a/src/CodeTracking.jl +++ b/src/CodeTracking.jl @@ -98,7 +98,7 @@ Otherwise `loc` will be `(filepath, line)`. """ function whereis(sf::StackTraces.StackFrame) sf.linfo === nothing && return nothing - return whereis(sf, sf.linfo.def) + return whereis(sf, getmethod(sf.linfo)) end """ diff --git a/src/utils.jl b/src/utils.jl index bd1c641..0d69bdb 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -179,7 +179,7 @@ function linerange(def::Expr) end linerange(arg) = linerange(convert(Expr, arg)) # Handle Revise's RelocatableExpr -function findline(ex, order) +function findline(ex::Expr, order) ex.head === :line && return ex.args[1], true for a in order(ex.args) a isa LineNumberNode && return a.line, true @@ -194,6 +194,55 @@ end fileline(lin::LineInfoNode) = String(lin.file), lin.line fileline(lnn::LineNumberNode) = String(lnn.file), lnn.line +if VERSION ≥ v"1.12.0-DEV.173" # https://github.com/JuliaLang/julia/pull/52415 + function linetable_scopes(m::Method) + src = Base.uncompressed_ast(m) + lts = [Vector{Base.Compiler.IRShow.LineInfoNode}() for _ = eachindex(src.code)] + for pc = eachindex(src.code) + Base.IRShow.append_scopes!(lts[pc], pc, src.debuginfo, m) + end + return lts + end +else + function linetable_scopes(m::Method) + src = Base.uncompressed_ast(m) + lt, cl = src.linetable, src.codelocs + lts = [Vector{Core.LineInfoNode}() for _ = eachindex(src.code)] + for pc = eachindex(src.code) + iszero(cl[pc]) && continue + scope = lts[pc] + push!(scope, lt[cl[pc]]) + while (k = last(scope).inlined_at) != Int32(0) + push!(scope, lt[k]) + end + if length(scope) > 1 + reverse!(scope) + end + end + return lts + end +end +@doc """ + scopes = linetable_scopes(m::Method) + +Return an array of "scopes" for each statement in the lowered code for `m`. If +`src = Base.uncompressed_ast(m)`, then `scopes[pc]` is an vector of +`LineInfoNode` objects that represent the scopes active at the statement at +position `pc` in `src.code`. + +`scopes[pc]` may have length larger than 1, where the first entry is for the +source location in `m`, and any later entries reflect code from inlining. + +The precise type of these entries varies with Julia version, +`Base.Compiler.IRShow.LineInfoNode` objects on Julia 1.12 and up, and +`Core.LineInfoNode` objects on earlier versions. These objects differ in some of +their fields. `:method`, `:file`, and `:line` are common to both types. +""" linetable_scopes + +getmethod(m::Method) = m +getmethod(mi::Core.MethodInstance) = getmethod(mi.def) +getmethod(ci::Core.CodeInstance) = getmethod(ci.def) + # This regex matches the pseudo-file name of a REPL history entry. const rREPL = r"^REPL\[(\d+)\]$" # Match anonymous function names diff --git a/test/runtests.jl b/test/runtests.jl index 5d31bee..c448802 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -129,13 +129,13 @@ isdefined(Main, :Revise) ? Main.Revise.includet("script.jl") : include("script.j @info "hello" end m = first(methods(f150)) - src = Base.uncompressed_ast(m) - idx = findfirst(lin -> String(lin.file) == @__FILE__, src.linetable) - lin = src.linetable[idx] + scopes = CodeTracking.linetable_scopes(m) + idx = findfirst(sc -> all(lin -> String(lin.file) == @__FILE__, sc), scopes) + lin = first(scopes[idx]) file, line = whereis(lin, m) @test endswith(file, String(lin.file)) - idx = findfirst(lin -> String(lin.file) != @__FILE__, src.linetable) - lin = src.linetable[idx] + idx = findfirst(sc -> !all(lin -> String(lin.file) == @__FILE__, sc), scopes) + lin = first(scopes[idx]) file, line = whereis(lin, m) if !Sys.iswindows() @test endswith(file, String(lin.file))