Skip to content

Commit c04924e

Browse files
authored
make show_backtrace and display_error work for Frame (#211)
make show_backtrace and display_error work for Frame
1 parent 1ddbf55 commit c04924e

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

src/types.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ Fields:
142142
- `pc`: the program counter (integer index of the next statment to be evaluated) for this frame
143143
- `caller`: the parent caller of this frame, or `nothing`
144144
- `callee`: the frame called by this one, or `nothing`
145+
146+
The `Base` functions `show_backtrace` and `display_error` are overloaded such that
147+
`show_backtrace(io::IO, frame::Frame)` and `display_error(io::IO, er, frame::Frame)`
148+
shows a backtrace or error, respectively, in a similar way as to how Base shows
149+
them.
145150
"""
146151
mutable struct Frame
147152
framecode::FrameCode

src/utils.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,3 +338,47 @@ function show_stackloc(io::IO, frame)
338338
end
339339
end
340340
show_stackloc(frame) = show_stackloc(stdout, frame)
341+
342+
# Printing of stacktraces and errors with Frame
343+
function Base.StackTraces.StackFrame(frame::Frame)
344+
if scopeof(frame) isa Method
345+
method = frame.framecode.scope
346+
method_args = something.(frame.framedata.locals[1:method.nargs])
347+
atypes = Tuple{typeof.(method_args)...}
348+
sig = method.sig
349+
sparams = Core.svec(frame.framedata.sparams...)
350+
mi = Core.Compiler.code_for_method(method, atypes, sparams, typemax(UInt))
351+
else
352+
mi = frame.framecode.src
353+
end
354+
Base.StackFrame(
355+
frame.framecode.scope.name,
356+
Symbol(JuliaInterpreter.getfile(frame)),
357+
JuliaInterpreter.linenumber(frame),
358+
mi,
359+
false,
360+
false,
361+
C_NULL
362+
)
363+
end
364+
365+
function Base.show_backtrace(io::IO, frame::Frame)
366+
stackframes = Tuple{Base.StackTraces.StackFrame, Int}[]
367+
while frame !== nothing
368+
push!(stackframes, (Base.StackTraces.StackFrame(frame), 1))
369+
frame = JuliaInterpreter.caller(frame)
370+
end
371+
print(io, "\nStacktrace:")
372+
try invokelatest(Base.update_stackframes_callback[], stackframes) catch end
373+
frame_counter = 0
374+
for (last_frame, n) in stackframes
375+
frame_counter += 1
376+
Base.show_trace_entry(IOContext(io, :backtrace => true), last_frame, n, prefix = string(" [", frame_counter, "] "))
377+
end
378+
end
379+
380+
function Base.display_error(io::IO, er, frame::Frame)
381+
printstyled(io, "ERROR: "; bold=true, color=Base.error_color())
382+
showerror(IOContext(io, :limit => true), er, frame)
383+
println(io)
384+
end

test/interpret.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,21 @@ locs = JuliaInterpreter.locals(JuliaInterpreter.enter_call(foo, ""))
424424
@test @interpret subtypes(Main, Integer) == subtypes(Main, Integer)
425425
@test (@elapsed @interpret subtypes(Integer)) < 30
426426
@test (@elapsed @interpret subtypes(Main, Integer)) < 30
427+
428+
# Test showing stacktraces from frames
429+
g_1(x) = g_2(x)
430+
g_2(x) = g_3(x)
431+
g_3(x) = error("foo")
432+
line_g = @__LINE__
433+
try
434+
break_on(:error)
435+
frame, bp = @interpret g_1(2.0)
436+
stacktrace_lines = split(sprint(Base.display_error, bp.err, leaf(frame)), '\n')
437+
@test occursin(string("ERROR: ", sprint(showerror, ErrorException("foo"))), stacktrace_lines[1])
438+
@test occursin("[1] error(::String) at error.jl:", stacktrace_lines[3])
439+
@test occursin("[2] g_3(::Float64) at $(@__FILE__):$(line_g - 1)", stacktrace_lines[4])
440+
@test occursin("[3] g_2(::Float64) at $(@__FILE__):$(line_g - 2)", stacktrace_lines[5])
441+
@test occursin("[4] g_1(::Float64) at $(@__FILE__):$(line_g - 3)", stacktrace_lines[6])
442+
finally
443+
break_off(:error)
444+
end

0 commit comments

Comments
 (0)