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
21 changes: 1 addition & 20 deletions src/signatures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,10 @@ In this case, `lastpc == pc`.
If no 3-argument `:method` expression is found, `nothing` will be returned in place of `(mt, sigt)`.
"""
function signature(interp::Interpreter, frame::Frame, @nospecialize(stmt), pc::Int)
mod = moduleof(frame)
lastpc = frame.pc = pc
while !ismethod3(stmt) # wait for the 3-arg :method or 4-arg define_method
if isanonymous_typedef(stmt)
lastpc = pc = step_through_methoddef(interp, frame, stmt) # define an anonymous function
elseif is_Typeof_for_anonymous_methoddef(stmt, frame.framecode.src.code, mod)
return nothing, pc
else
lastpc = pc
pc = throw_if_breakpoint(step_expr!(interp, frame, stmt, true))
Expand All @@ -84,19 +81,6 @@ end
signature(interp::Interpreter, frame::Frame, pc::Int) = signature(interp, frame, pc_expr(frame, pc), pc)
signature(frame::Frame, pc::Int) = signature(RecursiveInterpreter(), frame, pc)

function is_Typeof_for_anonymous_methoddef(@nospecialize(stmt), code::Vector{Any}, mod::Module)
isexpr(stmt, :call) || return false
f = stmt.args[1]
isa(f, QuoteNode) || return false
f.value === Core.Typeof || return false
arg1 = stmt.args[2]
if isa(arg1, SSAValue)
arg1 = code[arg1.id]
end
arg1 isa Symbol || return false
return !isdefined(mod, arg1)
end

function minid(@nospecialize(node), stmts, id)
if isa(node, SSAValue)
id = min(id, node.id)
Expand All @@ -112,10 +96,7 @@ end

function signature_top(frame, stmt::Expr, pc)
@assert ismethod3(stmt)
if is_define_method_call_4arg(stmt)
return minid(stmt.args[4], frame.framecode.src.code, pc)
end
return minid(stmt.args[2], frame.framecode.src.code, pc)
return minid(method_sig(stmt), frame.framecode.src.code, pc)
end

function step_through_methoddef(interp::Interpreter, frame::Frame, @nospecialize(stmt))
Expand Down
22 changes: 16 additions & 6 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ function method_module(@nospecialize(stmt))
return nothing
end

# Extract the signature data from a method3 statement.
# For Expr(:method, name, sig, body) it's args[2]; for define_method(mod, name, sigdata, body) it's args[4].
function method_sig(@nospecialize(stmt))
if is_define_method_call_4arg(stmt)
return stmt.args[4]
else
return stmt.args[2]
end
end

# Extract the CodeInfo body from a method3 statement.
# For Expr(:method, name, sig, body) it's args[3]; for define_method(mod, name, sigdata, body) it's args[5].
function method_body(@nospecialize(stmt))
Expand All @@ -142,24 +152,24 @@ function ismethod_with_name(src::CodeInfo, @nospecialize(stmt), target::Abstract
else
ismethod3(stmt) || return false
name = method_name(stmt)
if name === nothing && isexpr(stmt, :method)
name = stmt.args[2]
if name === nothing
name = method_sig(stmt)
end
end
isdone = false
while !isdone
if name isa AnySSAValue || name isa AnySlotNumber
name = src.code[name.id]
elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.svec)
elseif isexpr(name, :call) && callee_matches(name.args[1], Core, :svec)
name = name.args[2]
elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.Typeof)
elseif isexpr(name, :call) && callee_matches(name.args[1], Core, :Typeof)
name = name.args[2]
elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.apply_type)
elseif isexpr(name, :call) && callee_matches(name.args[1], Core, :apply_type)
for arg in name.args[2:end]
ismethod_with_name(src, arg, target; reentrant=true) && return true
end
isdone = true
elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], UnionAll)
elseif isexpr(name, :call) && callee_matches(name.args[1], Core, :UnionAll)
for arg in name.args[2:end]
ismethod_with_name(src, arg, target; reentrant=true) && return true
end
Expand Down
18 changes: 18 additions & 0 deletions test/signatures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,24 @@ end
@test LoweredCodeUtils.identify_framemethod_calls(frame) isa Any # must not throw
end

module FoldedCallees
struct Callable end
end

@testset "Callees with and without JuliaInterpreter's const folding" begin
# JuliaInterpreter folds `const` callees such as `Core.svec` into `QuoteNode`s only when
# it optimizes the frame, and not in top-level code on Julia 1.12+; otherwise they remain
# `GlobalRef`s. Both forms must be recognized.
lwr = Meta.lower(FoldedCallees, :((::Callable)(x) = x))
for optimize in (true, false)
src = Frame(FoldedCallees, lwr.args[1]::CodeInfo; optimize).framecode.src
# the method has no name, so its signature is walked to find `Callable`
@test any(src.code) do stmt
LoweredCodeUtils.ismethod3(stmt) && LoweredCodeUtils.ismethod_with_name(src, stmt, "Callable")
end
end
end

module BreakpointRefTest end

@testset "BreakpointRef from JuliaInterpreter is an error, not a pc" begin
Expand Down
Loading