diff --git a/src/signatures.jl b/src/signatures.jl index 0f90bcb..7cda176 100644 --- a/src/signatures.jl +++ b/src/signatures.jl @@ -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)) @@ -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) @@ -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)) diff --git a/src/utils.jl b/src/utils.jl index f7a0047..0526918 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -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)) @@ -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 diff --git a/test/signatures.jl b/test/signatures.jl index 26c51fd..1f5570e 100644 --- a/test/signatures.jl +++ b/test/signatures.jl @@ -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