Skip to content

Commit 9f5d98d

Browse files
authored
Fix kwprep stepping in Julia 1.12 and 1.11 (#667)
Closes #620
1 parent 822632d commit 9f5d98d

File tree

3 files changed

+42
-14
lines changed

3 files changed

+42
-14
lines changed

src/commands.jl

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ maybe_step_through_wrapper!(frame::Frame) = maybe_step_through_wrapper!(finish_a
256256

257257
const kwhandler = Core.kwcall
258258
const kwextrastep = 0
259+
const kw_has_f_first = VERSION.major == 1 && VERSION.minor == 11
259260

260261
"""
261262
frame = maybe_step_through_kwprep!(recurse, frame)
@@ -273,21 +274,29 @@ function maybe_step_through_kwprep!(@nospecialize(recurse), frame::Frame, istopl
273274
pc, src = frame.pc, frame.framecode.src
274275
n = length(src.code)
275276
stmt = pc_expr(frame, pc)
277+
if isbindingresolved_deprecated && !isa(stmt, Tuple{Symbol,Vararg{Symbol}}) && !is_empty_namedtuple(stmt) && n >= pc+1
278+
pc += 1
279+
stmt = pc_expr(frame, pc)
280+
elseif kw_has_f_first && pc < n && is_empty_namedtuple(pc_expr(frame, pc+1)) && isa(stmt, QuoteNode)
281+
pc = step_expr!(recurse, frame, istoplevel)
282+
stmt = pc_expr(frame, pc)
283+
end
276284
if isa(stmt, Tuple{Symbol,Vararg{Symbol}})
277285
# Check to see if we're creating a NamedTuple followed by kwfunc call
278-
pccall = pc + 4 + kwextrastep
286+
pccall = pc + 4 + kwextrastep + isbindingresolved_deprecated
279287
if pccall <= n
280288
stmt1 = src.code[pc+1]
281289
# We deliberately check isexpr(stmt, :call) rather than is_call(stmt): if it's
282290
# assigned to a local, it's *not* kwarg preparation.
283-
if isexpr(stmt1, :call) && is_quotenode_egal(stmt1.args[1], Core.apply_type) && is_quoted_type(stmt1.args[2], :NamedTuple)
284-
stmt4, stmt5 = src.code[pc+4], src.code[pc+5]
285-
if isexpr(stmt4, :call) && is_quotenode_egal(stmt4.args[1], kwhandler)
291+
if isexpr(stmt1, :call) && ((is_quotenode_egal(stmt1.args[1], Core.apply_type) && is_quoted_type(stmt1.args[2], :NamedTuple)) ||
292+
(is_global_ref(stmt1.args[1], Core, :apply_type) && is_global_ref(stmt1.args[2], Core, :NamedTuple)))
293+
stmt4, stmt5 = src.code[pc+4+isbindingresolved_deprecated], src.code[pc+5+isbindingresolved_deprecated]
294+
if isexpr(stmt4, :call) && (is_quotenode_egal(stmt4.args[1], kwhandler) || is_global_ref(stmt4.args[1], Core, :kwcall))
286295
while pc < pccall
287296
pc = step_expr!(recurse, frame, istoplevel)
288297
end
289298
return frame
290-
elseif isexpr(stmt5, :call) && is_quotenode_egal(stmt5.args[1], kwhandler) && pccall+1 <= n
299+
elseif isexpr(stmt5, :call) && (is_quotenode_egal(stmt5.args[1], kwhandler) || is_global_ref(stmt5.args[1], Core, :kwcall)) && pccall+1 <= n
291300
# This happens when the call is scoped by a module
292301
pccall += 1
293302
while pc < pccall
@@ -298,13 +307,13 @@ function maybe_step_through_kwprep!(@nospecialize(recurse), frame::Frame, istopl
298307
end
299308
end
300309
end
301-
elseif isexpr(stmt, :call) && is_quoted_type(stmt.args[1], :NamedTuple) && length(stmt.args) == 1
310+
elseif is_empty_namedtuple(stmt)
302311
# Creating an empty NamedTuple, now split by type (no supplied kwargs vs kwargs...)
303312
if pc + 1 <= n
304313
stmt1 = src.code[pc+1]
305314
if isexpr(stmt1, :call)
306315
f = stmt1.args[1]
307-
if is_quotenode_egal(f, Base.pairs)
316+
if is_quotenode_egal(f, Base.pairs) || is_global_ref(f, Base, :pairs)
308317
# No supplied kwargs
309318
pcsplat = pc + 3
310319
if pcsplat <= n
@@ -327,14 +336,14 @@ function maybe_step_through_kwprep!(@nospecialize(recurse), frame::Frame, istopl
327336
end
328337
end
329338
end
330-
elseif is_quotenode_egal(f, Base.merge) && ((pccall = pc + 7) <= n)
339+
elseif (is_quotenode_egal(f, Base.merge) || is_global_ref(f, Base, :merge)) && ((pccall = pc + 7 + 2*isbindingresolved_deprecated) <= n)
331340
stmtk = src.code[pccall-1]
332-
if isexpr(stmtk, :call) && is_quotenode_egal(stmtk.args[1], kwhandler)
333-
for i = 1:4
341+
if isexpr(stmtk, :call) && (is_quotenode_egal(stmtk.args[1], kwhandler) || is_global_ref(stmtk.args[1], Core, :kwcall))
342+
for i = 1:4 + isbindingresolved_deprecated
334343
pc = step_expr!(recurse, frame, istoplevel)
335344
end
336345
stmti = src.code[pc]
337-
if isexpr(stmti, :call) && is_quotenode_egal(stmti.args[1], #= deliberately not kwhandler =# Core.kwfunc)
346+
if isexpr(stmti, :call) && (is_quotenode_egal(stmti.args[1], #= deliberately not kwhandler =# Core.kwfunc) || is_global_ref(stmti.args[1], Core, :kwfunc))
338347
pc = step_expr!(recurse, frame, istoplevel)
339348
end
340349
end
@@ -347,6 +356,17 @@ end
347356
maybe_step_through_kwprep!(frame::Frame, istoplevel::Bool=false) =
348357
maybe_step_through_kwprep!(finish_and_return!, frame, istoplevel)
349358

359+
@static if isbindingresolved_deprecated
360+
function is_empty_namedtuple(stmt)
361+
isexpr(stmt, :call) && length(stmt.args) == 1 || return false
362+
arg1 = stmt.args[1]
363+
isa(arg1, GlobalRef) || return false
364+
return arg1.name === :NamedTuple
365+
end
366+
else
367+
is_empty_namedtuple(stmt) = isexpr(stmt, :call) && is_quoted_type(stmt.args[1], :NamedTuple) && length(stmt.args) == 1
368+
end
369+
350370
"""
351371
ret = maybe_reset_frame!(recurse, frame, pc, rootistoplevel)
352372

src/utils.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ is_call_or_return(@nospecialize(node)) = is_call(node) || node isa ReturnNode
168168
is_dummy(bpref::BreakpointRef) = bpref.stmtidx == 0 && bpref.err === nothing
169169

170170
function unpack_splatcall(stmt)
171-
if isexpr(stmt, :call) && length(stmt.args) >= 3 && is_quotenode_egal(stmt.args[1], Core._apply_iterate)
171+
if isexpr(stmt, :call) && length(stmt.args) >= 3 && (is_quotenode_egal(stmt.args[1], Core._apply_iterate) || is_global_ref(stmt.args[1], Core, :_apply_iterate))
172172
return true, stmt.args[3]
173173
end
174174
return false, nothing
@@ -184,6 +184,8 @@ end
184184
function is_bodyfunc(@nospecialize(arg))
185185
if isa(arg, QuoteNode)
186186
arg = arg.value
187+
elseif isa(arg, GlobalRef)
188+
arg = getproperty(arg.mod, arg.name)
187189
end
188190
if isa(arg, Function)
189191
fname = String((typeof(arg).name::Core.TypeName).name)

test/debug.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ end
8181
oframe = frame = enter_call(func, args...; kwargs...)
8282
frame = JuliaInterpreter.maybe_step_through_kwprep!(frame, false)
8383
frame = JuliaInterpreter.maybe_step_through_wrapper!(frame)
84-
@test JuliaInterpreter.hasarg(JuliaInterpreter.isidentical(QuoteNode(==)), frame.framecode.src.code)
84+
@test JuliaInterpreter.hasarg(JuliaInterpreter.isidentical(QuoteNode(==)), frame.framecode.src.code) || JuliaInterpreter.hasarg(JuliaInterpreter.isidentical(GlobalRef(@__MODULE__, :(==))), frame.framecode.src.code)
8585
f, pc = debug_command(frame, :n)
8686
@test f === frame
8787
@test isa(pc, Int)
@@ -426,17 +426,23 @@ end
426426

427427
frame = JuliaInterpreter.enter_call(sort, a)
428428
frame = stepkw!(frame)
429-
@test frame.pc == JuliaInterpreter.nstatements(frame.framecode) - 1 broken=VERSIONv"1.11-"
429+
@test frame.pc == JuliaInterpreter.nstatements(frame.framecode) - 1
430430

431431
frame, pc = debug_command(frame, :s)
432432
frame, pc = debug_command(frame, :se) # get past copymutable
433+
if JuliaInterpreter.isbindingresolved_deprecated
434+
frame, pc = debug_command(frame, :se) # there's an extra line for the GlobalRef
435+
end
433436
frame = stepkw!(frame)
434437
@test frame.pc > 4
435438

436439
frame = JuliaInterpreter.enter_call(sort, a; rev=true)
437440
frame, pc = debug_command(frame, :se)
438441
frame, pc = debug_command(frame, :s)
439442
frame, pc = debug_command(frame, :se) # get past copymutable
443+
if JuliaInterpreter.isbindingresolved_deprecated
444+
frame, pc = debug_command(frame, :se) # there's an extra line for the GlobalRef
445+
end
440446
frame = stepkw!(frame)
441447
@test frame.pc == JuliaInterpreter.nstatements(frame.framecode) - 1
442448
end

0 commit comments

Comments
 (0)