Skip to content

Commit

Permalink
Add bodyfunction(m::Method) (#36385)
Browse files Browse the repository at this point in the history
This finds the function that implements the "body" of keyword-method `m`,
as generated by lowering.

This addresses the "discovery" part of #30908.
The renaming is done by LoweredCodeUtils.
  • Loading branch information
timholy committed Aug 27, 2020
1 parent f047d7f commit e0cc82b
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 0 deletions.
45 changes: 45 additions & 0 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,51 @@ function hasmethod(@nospecialize(f), @nospecialize(t), kwnames::Tuple{Vararg{Sym
return issubset(kwnames, kws)
end

"""
fbody = bodyfunction(basemethod::Method)
Find the keyword "body function" (the function that contains the body of the method
as written, called after all missing keyword-arguments have been assigned default values).
`basemethod` is the method you obtain via [`which`](@ref) or [`methods`](@ref).
"""
function bodyfunction(basemethod::Method)
function getsym(arg)
isa(arg, Symbol) && return arg
isa(arg, GlobalRef) && return arg.name
return nothing
end

fmod = basemethod.module
# The lowered code for `basemethod` should look like
# %1 = mkw(kwvalues..., #self#, args...)
# return %1
# where `mkw` is the name of the "active" keyword body-function.
ast = Base.uncompressed_ast(basemethod)
f = nothing
if isa(ast, Core.CodeInfo) && length(ast.code) >= 2
callexpr = ast.code[end-1]
if isa(callexpr, Expr) && callexpr.head == :call
fsym = callexpr.args[1]
if isa(fsym, Symbol)
f = getfield(fmod, fsym)
elseif isa(fsym, GlobalRef)
newsym = nothing
if fsym.mod === Core && fsym.name === :_apply
newsym = getsym(callexpr.args[2])
elseif fsym.mod === Core && fsym.name === :_apply_iterate
newsym = getsym(callexpr.args[3])
end
if isa(newsym, Symbol)
f = getfield(basemethod.module, newsym)::Function
else
f = getfield(fsym.mod, fsym.name)::Function
end
end
end
end
return f
end

"""
Base.isambiguous(m1, m2; ambiguous_bottom=false) -> Bool
Expand Down
16 changes: 16 additions & 0 deletions test/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -909,3 +909,19 @@ end

@test length(methods(g, ())) == 1
end

module BodyFunctionLookup
f1(x, y; a=1) = error("oops")
f2(f::Function, args...; kwargs...) = f1(args...; kwargs...)
end

@testset "bodyfunction" begin
m = first(methods(BodyFunctionLookup.f1))
f = Base.bodyfunction(m)
@test occursin("f1#", String(nameof(f)))
m = first(methods(BodyFunctionLookup.f2))
f = Base.bodyfunction(m)
@test f !== Core._apply_iterate
@test f !== Core._apply
@test occursin("f2#", String(nameof(f)))
end

0 comments on commit e0cc82b

Please sign in to comment.