Skip to content

Commit 60edb33

Browse files
timholysimeonschaub
authored andcommitted
Add bodyfunction(m::Method) (JuliaLang#36385)
This finds the function that implements the "body" of keyword-method `m`, as generated by lowering. This addresses the "discovery" part of JuliaLang#30908. The renaming is done by LoweredCodeUtils.
1 parent c0ae551 commit 60edb33

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

base/reflection.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,51 @@ function hasmethod(@nospecialize(f), @nospecialize(t), kwnames::Tuple{Vararg{Sym
12801280
return issubset(kwnames, kws)
12811281
end
12821282

1283+
"""
1284+
fbody = bodyfunction(basemethod::Method)
1285+
1286+
Find the keyword "body function" (the function that contains the body of the method
1287+
as written, called after all missing keyword-arguments have been assigned default values).
1288+
`basemethod` is the method you obtain via [`which`](@ref) or [`methods`](@ref).
1289+
"""
1290+
function bodyfunction(basemethod::Method)
1291+
function getsym(arg)
1292+
isa(arg, Symbol) && return arg
1293+
isa(arg, GlobalRef) && return arg.name
1294+
return nothing
1295+
end
1296+
1297+
fmod = basemethod.module
1298+
# The lowered code for `basemethod` should look like
1299+
# %1 = mkw(kwvalues..., #self#, args...)
1300+
# return %1
1301+
# where `mkw` is the name of the "active" keyword body-function.
1302+
ast = Base.uncompressed_ast(basemethod)
1303+
f = nothing
1304+
if isa(ast, Core.CodeInfo) && length(ast.code) >= 2
1305+
callexpr = ast.code[end-1]
1306+
if isa(callexpr, Expr) && callexpr.head == :call
1307+
fsym = callexpr.args[1]
1308+
if isa(fsym, Symbol)
1309+
f = getfield(fmod, fsym)
1310+
elseif isa(fsym, GlobalRef)
1311+
newsym = nothing
1312+
if fsym.mod === Core && fsym.name === :_apply
1313+
newsym = getsym(callexpr.args[2])
1314+
elseif fsym.mod === Core && fsym.name === :_apply_iterate
1315+
newsym = getsym(callexpr.args[3])
1316+
end
1317+
if isa(newsym, Symbol)
1318+
f = getfield(basemethod.module, newsym)::Function
1319+
else
1320+
f = getfield(fsym.mod, fsym.name)::Function
1321+
end
1322+
end
1323+
end
1324+
end
1325+
return f
1326+
end
1327+
12831328
"""
12841329
Base.isambiguous(m1, m2; ambiguous_bottom=false) -> Bool
12851330

test/reflection.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,3 +909,19 @@ end
909909

910910
@test length(methods(g, ())) == 1
911911
end
912+
913+
module BodyFunctionLookup
914+
f1(x, y; a=1) = error("oops")
915+
f2(f::Function, args...; kwargs...) = f1(args...; kwargs...)
916+
end
917+
918+
@testset "bodyfunction" begin
919+
m = first(methods(BodyFunctionLookup.f1))
920+
f = Base.bodyfunction(m)
921+
@test occursin("f1#", String(nameof(f)))
922+
m = first(methods(BodyFunctionLookup.f2))
923+
f = Base.bodyfunction(m)
924+
@test f !== Core._apply_iterate
925+
@test f !== Core._apply
926+
@test occursin("f2#", String(nameof(f)))
927+
end

0 commit comments

Comments
 (0)