Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ Sockets = "< 0.0.1, 1"
Pkg = "< 0.0.1, 1"
Base64 = "< 0.0.1, 1"
julia = "1"
JuliaInterpreter = "0.8.5, 0.9, 0.10, 0.11"
JuliaInterpreter = "0.8.5, 0.9, 0.10, 0.11, 0.12"
JSON = "0.20, 0.21, 1"
11 changes: 10 additions & 1 deletion src/DebugEngines.jl
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
for m in methods(Base.unwrap_unionall(obj))
push!(JuliaInterpreter.interpreted_methods, m)
end
catch err

Check notice on line 191 in src/DebugEngines.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_binding

Variable has been assigned but not used.
@warn "Setting $obj as an interpreted method failed."
end
elseif obj isa Module
Expand All @@ -206,7 +206,7 @@
for m in methods(Base.unwrap_unionall(obj))
push!(JuliaInterpreter.compiled_methods, m)
end
catch err

Check notice on line 209 in src/DebugEngines.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_binding

Variable has been assigned but not used.
@warn "Setting $obj as an interpreted method failed."
end
elseif obj isa Module
Expand Down Expand Up @@ -238,7 +238,7 @@
delete!(bps, bp)

@debug "Setting function breakpoint for '$(bp.name)' succeeded."
catch err

Check notice on line 241 in src/DebugEngines.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_binding

Variable has been assigned but not used.
@debug "Setting function breakpoint for '$(bp.name)' failed."
end
end
Expand Down Expand Up @@ -322,7 +322,16 @@
else
false
end
frame.framecode.scope isa Module && (JuliaInterpreter.isexpr(JuliaInterpreter.pc_expr(frame), :return) || is_a_return_node)
(JuliaInterpreter.isexpr(JuliaInterpreter.pc_expr(frame), :return) || is_a_return_node) || return false
# Only a return of the user's top-level code itself ends the top-level expression. Since
# JuliaInterpreter 0.12, code that a method evaluates with `Core.eval` runs in frames
# with a `Module` scope too, below the method's frame, and returning from them
# continues the method.
while frame !== nothing
frame.framecode.scope isa Module || return false
frame = frame.caller
end
return true
end

function our_debug_command(debug_engine::DebugEngine, cmd::Symbol)
Expand Down Expand Up @@ -369,7 +378,7 @@
StopReasonStep
end

is_valid_expression(x) = true # atom

Check notice on line 381 in src/DebugEngines.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_function_argument

An argument is included in a function signature but not used within its body.
is_valid_expression(::Nothing) = false # empty
is_valid_expression(ex::Expr) = !Meta.isexpr(ex, (:incomplete, :error))

Expand Down Expand Up @@ -493,7 +502,7 @@
debug_engine.last_exception = ret.err
error_msg = try
Base.invokelatest(sprint, Base.showerror, ret.err)
catch err

Check notice on line 505 in src/DebugEngines.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_binding

Variable has been assigned but not used.
"Error while displaying the original error."
end
debug_engine.on_stop(StopReasonException, error_msg)
Expand Down
38 changes: 36 additions & 2 deletions test/test_debugengine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ end
# Runs `code`, saved as a real file so file breakpoints can match it, in an engine
# configured the way the extension does by default, and reports each stop on `stops`,
# which is closed once the run ends.
function start_paused_engine(code, breakpoint_lines)
function start_paused_engine(code, breakpoint_lines; compiled_items = ["ALL_MODULES_EXCEPT_MAIN"])
JuliaInterpreter.remove()
file = joinpath(mktempdir(), "paused.jl")
write(file, code)
Expand All @@ -136,7 +136,7 @@ end
de = DebugEngines.DebugEngine(Module(:Paused), code, file, false, (reason, _...) -> put!(stops, reason))
# `ALL_MODULES_EXCEPT_MAIN` can never be applied for good, so it is retried after
# every step, which is the path that used to throw away the paused framecodes.
DebugEngines.set_compiled_functions_modules!(de, ["ALL_MODULES_EXCEPT_MAIN"])
DebugEngines.set_compiled_functions_modules!(de, compiled_items)
task = @async try
run(de)
finally
Expand Down Expand Up @@ -227,3 +227,37 @@ end
JuliaInterpreter.remove()
end
end

@testitem "stepping through code a method evaluates continues the method" setup=[PausedEngine] begin
# Since JuliaInterpreter 0.12, the code passed to `Core.eval` is interpreted in frames
# with a `Module` scope below the calling method's frame. Pausing at their return must
# not be taken for the end of the top-level expression, which would drop the method.
# `Core` has to stay interpreted for that, so the modules are not compiled here.
code = """
function f()
Core.eval(@__MODULE__, :(evaluated = true))
global after_eval = true
return nothing
end
f()
"""
de, file, stops, task = start_paused_engine(code, [2]; compiled_items = String[])
try
@test next_stop(stops) == DebugEngines.StopReasonBreakpoint
@test paused_line(de) == 2

reason = nothing
for _ in 1:200
DebugEngines.execution_step_in(de, missing)
reason = next_stop(stops)
reason === :finished && break
end
@test reason === :finished
@test Base.invokelatest(isdefined, de.mod, :evaluated)
@test Base.invokelatest(isdefined, de.mod, :after_eval)
finally
DebugEngines.terminate(de)
wait(task)
JuliaInterpreter.remove()
end
end
Loading