diff --git a/src/interpret.jl b/src/interpret.jl index e3613bf5..9bcf1d30 100644 --- a/src/interpret.jl +++ b/src/interpret.jl @@ -460,13 +460,24 @@ function coverage_visit_line!(frame::Frame) pc, code = frame.pc, frame.framecode code.report_coverage || return src = code.src - codeloc = src.codelocs[pc] - if codeloc != frame.last_codeloc && codeloc != 0 - linetable = src.linetable::Vector{Any} - lineinfo = linetable[codeloc]::Core.LineInfoNode - file, line = String(lineinfo.file), lineinfo.line - ccall(:jl_coverage_visit_line, Cvoid, (Cstring, Csize_t, Cint), file, sizeof(file), line) - frame.last_codeloc = codeloc + if isdefined(src, :debuginfo) # VERSION >= v"1.12" + lineinfo = linetable(src.debuginfo, pc) + file, line = lineinfo.file, lineinfo.line + if line != frame.last_codeloc + file isa Symbol || (file = Symbol(file)) + ccall(:jl_coverage_visit_line, Cvoid, (Cstring, Csize_t, Cint), file, sizeof(file), line) + frame.last_codeloc = codeloc + end + else + codeloc = src.codelocs[pc] + if codeloc != frame.last_codeloc && codeloc != 0 + linetable = src.linetable::Vector{Any} + lineinfo = linetable[codeloc]::Core.LineInfoNode + file, line = lineinfo.file, lineinfo.line + file isa Symbol || (file = Symbol(file)) + ccall(:jl_coverage_visit_line, Cvoid, (Cstring, Csize_t, Cint), file, sizeof(file), line) + frame.last_codeloc = codeloc + end end end diff --git a/src/types.jl b/src/types.jl index 04539fa9..00ea34fc 100644 --- a/src/types.jl +++ b/src/types.jl @@ -147,8 +147,23 @@ function FrameCode(scope, src::CodeInfo; generator=false, optimize=true) lt = linetable(src) unique_files = Set{Symbol}() - for entry in lt - push!(unique_files, entry.file) + if isdefined(src, :debuginfo) # VERSION >= v"1.12" + function pushuniquefiles!(unique_files, lt) + for edge in lt.edges + pushuniquefiles!(unique_files, edge) + end + linetable = lt.linetable + if linetable === nothing + push!(unique_files, Base.IRShow.debuginfo_file1(lt)) + else + pushuniquefiles!(unique_files, linetable) + end + end + pushuniquefiles!(unique_files, lt) + else + for entry in lt + push!(unique_files, entry.file) + end end framecode = FrameCode(scope, src, methodtables, breakpoints, slotnamelists, used, generator, report_coverage, unique_files) @@ -237,7 +252,7 @@ mutable struct Frame assignment_counter::Int64 caller::Union{Frame,Nothing} callee::Union{Frame,Nothing} - last_codeloc::Int32 + last_codeloc::Int end function Frame(framecode::FrameCode, framedata::FrameData, pc=1, caller=nothing) if length(junk_frames) > 0 diff --git a/src/utils.jl b/src/utils.jl index 7923d5f8..09a28c4c 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -266,18 +266,62 @@ function linetable(arg) if isa(arg, FrameCode) arg = arg.src end - return (arg::CodeInfo).linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264 + ci = arg::CodeInfo + if isdefined(ci, :debuginfo) # VERSION >= v1.12 + return ci.debuginfo + else + return ci.linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264 + end end _linetable(list::Vector, i::Integer) = list[i]::Union{Expr,LineTypes} function linetable(arg, i::Integer; macro_caller::Bool=false)::Union{Expr,LineTypes} lt = linetable(arg) - lineinfo = _linetable(lt, i) - if macro_caller - while lineinfo isa Core.LineInfoNode && lineinfo.method === Symbol("macro expansion") && lineinfo.inlined_at != 0 - lineinfo = _linetable(lt, lineinfo.inlined_at) + if isdefined(Core, :DebugInfo) + # TODO: decode the linetable at this frame efficiently by reimplementing this here + # TODO: get the contextual name from the parent, rather than returning "n/a" (which breaks Cthulhu) + return Base.IRShow.buildLineInfoNode(lt, :var"n/a", i)[1] # ignore all inlining / macro expansion / etc :( + else + lineinfo = _linetable(lt, i) + if macro_caller + while lineinfo isa Core.LineInfoNode && lineinfo.method === Symbol("macro expansion") && lineinfo.inlined_at != 0 + lineinfo = _linetable(lt, lineinfo.inlined_at) + end end + return lineinfo + end +end + +if isdefined(Core, :DebugInfo) +function linetable_max(lt::Core.DebugInfo) + while true + ltnext = lt.linetable + ltnext === nothing && break + lt = ltnext + end + lastline = 0 + for k = 0:typemax(Int) + codeloc = Base.IRShow.getdebugidx(lt, k) + line::Int = codeloc[1] + line < 0 && break + lastline = max(lastline, line) end - return lineinfo + return lastline +end +function codelocs(arg, i::Integer) + lt = linetable(arg) + codeloc = Base.IRShow.getdebugidx(lt, i) + line::Int = codeloc[1] + line < 0 && return 0 # broken or disabled debug info? + if line == 0 && codeloc[2] == 0 + return 0 # no line number update + end + return Int(i) +end + +else + +function linetable_max(lt::Vector) + return getline(lt[end]) end function codelocs(arg) @@ -289,7 +333,8 @@ function codelocs(arg) end return (arg::CodeInfo).codelocs::Vector{Int32} end -codelocs(arg, i::Integer) = codelocs(arg)[i] # for consistency with linetable (but no extra benefit here) +codelocs(arg, i::Integer) = codelocs(arg)[i] +end function lineoffset(framecode::FrameCode) offset = 0 @@ -367,7 +412,7 @@ function codelocation(code::CodeInfo, idx::Int) idx′ = idx # look ahead if we are on a meta line while idx′ < length(code.code) - codeloc = codelocs(code)[idx′] + codeloc = codelocs(code, idx′) codeloc == 0 || return codeloc ex = code.code[idx′] ex === nothing || isexpr(ex, :meta) || break @@ -377,7 +422,7 @@ function codelocation(code::CodeInfo, idx::Int) # if zero, look behind until we find where we last might have had a line while idx′ > 0 ex = code.code[idx′] - codeloc = codelocs(code)[idx′] + codeloc = codelocs(code, idx′) codeloc == 0 || return codeloc idx′ -= 1 end @@ -390,8 +435,8 @@ function compute_corrected_linerange(method::Method) offset = line1 - method.line @assert !is_generated(method) src = JuliaInterpreter.get_source(method) - lastline = linetable(src)[end]::LineTypes - return line1:getline(lastline) + offset + lastline = linetable_max(linetable(src)) + return line1:lastline + offset end function compute_linerange(framecode) @@ -508,7 +553,7 @@ function print_framecode(io::IO, framecode::FrameCode; pc=0, range=1:nstatements ndstmt = ndigits(nstatements(framecode)) lt = linetable(framecode) offset = lineoffset(framecode) - ndline = isempty(lt) ? 0 : ndigits(getline(lt[end]) + offset) + ndline = ndigits(linetable_max(lt) + offset) nullline = " "^ndline src = copy(framecode.src) replace_coretypes!(src; rev=true)