diff --git a/src/construct.jl b/src/construct.jl index 4615054d..71352f8e 100644 --- a/src/construct.jl +++ b/src/construct.jl @@ -172,7 +172,7 @@ function prepare_framecode(method::Method, @nospecialize(argtypes); enter_genera if (!isempty(lenv) && (hasarg(isidentical(:llvmcall), code.code) || hasarg(isidentical(Base.llvmcall), code.code) || hasarg(a->is_global_ref(a, Base, :llvmcall), code.code))) || - hasarg(isidentical(:iolock_begin), code.code) + hasarg(isidentical(:iolock_begin), code.code) return Compiled() end framecode = FrameCode(method, code; generator=generator) diff --git a/src/interpret.jl b/src/interpret.jl index e3613bf5..4196e517 100644 --- a/src/interpret.jl +++ b/src/interpret.jl @@ -460,14 +460,25 @@ function coverage_visit_line!(frame::Frame) pc, code = frame.pc, frame.framecode code.report_coverage || return src = code.src + @static if VERSION ≥ v"1.12.0-DEV.173" + lineinfo = linetable(src.debuginfo, pc) + file, line = lineinfo.file, lineinfo.line + if line != frame.last_codeloc + file isa Symbol || (file = Symbol(file)::Symbol) + @ccall jl_coverage_visit_line(file::Cstring, sizeof(file)::Csize_t, line::Cint)::Cvoid + frame.last_codeloc = codeloc + end + else # VERSION < v"1.12.0-DEV.173" 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) + file, line = lineinfo.file, lineinfo.line + file isa Symbol || (file = Symbol(file)::Symbol) + @ccall jl_coverage_visit_line(file::Cstring, sizeof(file)::Csize_t, line::Cint)::Cvoid frame.last_codeloc = codeloc end + end # @static if end # For "profiling" where JuliaInterpreter spends its time. See the commented-out block diff --git a/src/types.jl b/src/types.jl index 04539fa9..041a5b84 100644 --- a/src/types.jl +++ b/src/types.jl @@ -147,9 +147,24 @@ function FrameCode(scope, src::CodeInfo; generator=false, optimize=true) lt = linetable(src) unique_files = Set{Symbol}() + @static if VERSION ≥ v"1.12.0-DEV.173" + function pushuniquefiles!(unique_files::Set{Symbol}, 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 # VERSION < v"1.12.0-DEV.173" for entry in lt push!(unique_files, entry.file) end + end # @static if framecode = FrameCode(scope, src, methodtables, breakpoints, slotnamelists, used, generator, report_coverage, unique_files) if scope isa Method @@ -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..8f4b14ad 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -258,7 +258,11 @@ end # These getters improve inference since fieldtype(CodeInfo, :linetable) # and fieldtype(CodeInfo, :codelocs) are both Any -const LineTypes = Union{LineNumberNode,Core.LineInfoNode} +@static if VERSION ≥ v"1.12.0-DEV.173" + const LineTypes = Union{LineNumberNode,Base.IRShow.LineInfoNode} +else + const LineTypes = Union{LineNumberNode,Core.LineInfoNode} +end function linetable(arg) if isa(arg, Frame) arg = arg.framecode @@ -266,30 +270,74 @@ 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 + @static if VERSION ≥ v"1.12.0-DEV.173" + return ci.debuginfo + else # VERSION < v"1.12.0-DEV.173" + return ci.linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264 + end # @static if 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) + @static if VERSION ≥ v"1.12.0-DEV.173" + # 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 # VERSION < v"1.12.0-DEV.173" + lin = lt[i]::Union{Expr,LineTypes} if macro_caller - while lineinfo isa Core.LineInfoNode && lineinfo.method === Symbol("macro expansion") && lineinfo.inlined_at != 0 - lineinfo = _linetable(lt, lineinfo.inlined_at) + while lin isa Core.LineInfoNode && lin.method === Symbol("macro expansion") && lin.inlined_at != 0 + lin = lt[lin.inlined_at]::Union{Expr,LineTypes} end end - return lineinfo + return lin + end # @static if end -function codelocs(arg) +@static if VERSION ≥ v"1.12.0-DEV.173" + +function getlastline(arg) + debuginfo = linetable(arg) + while true + ltnext = debuginfo.linetable + ltnext === nothing && break + debuginfo = ltnext + end + lastline = 0 + for k = 0:typemax(Int) + codeloc = Core.Compiler.getdebugidx(debuginfo, k) + line::Int = codeloc[1] + line < 0 && break + lastline = max(lastline, line) + end + 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 # VERSION < v"1.12.0-DEV.173" + +getlastline(arg) = getline(linetable(arg)[end]) +function codelocs(arg, i::Integer) if isa(arg, Frame) arg = arg.framecode end if isa(arg, FrameCode) arg = arg.src end - return (arg::CodeInfo).codelocs::Vector{Int32} + ci = arg::CodeInfo + return ci.codelocs[i] end -codelocs(arg, i::Integer) = codelocs(arg)[i] # for consistency with linetable (but no extra benefit here) + +end # @static if function lineoffset(framecode::FrameCode) offset = 0 @@ -367,7 +415,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 +425,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 +438,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 = getlastline(src) + return line1:lastline + offset end function compute_linerange(framecode) @@ -506,9 +554,7 @@ breakpointchar(framecode, stmtidx) = function print_framecode(io::IO, framecode::FrameCode; pc=0, range=1:nstatements(framecode), kwargs...) iscolor = get(io, :color, false) ndstmt = ndigits(nstatements(framecode)) - lt = linetable(framecode) - offset = lineoffset(framecode) - ndline = isempty(lt) ? 0 : ndigits(getline(lt[end]) + offset) + ndline = ndigits(getlastline(linetable(framecode)) + lineoffset(framecode)) nullline = " "^ndline src = copy(framecode.src) replace_coretypes!(src; rev=true) diff --git a/test/utils.jl b/test/utils.jl index 1a3f058e..f1cf69a7 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -36,8 +36,7 @@ struct Aborted # for signaling that some statement or test blocks were interr end function Aborted(frame::Frame, pc) - src = frame.framecode.src - lineidx = src.codelocs[pc] + lineidx = JuliaInterpreter.codelocs(frame, pc) lineinfo = JuliaInterpreter.linetable(frame, lineidx; macro_caller=true) return Aborted(lineinfo) end