Skip to content

Commit 3b60b6a

Browse files
serenity4aviatesk
andauthored
methoddef!: use mt => sig format when filling in signatures (#125)
* Parametrize signatures by method table * Add tests * Update src/signatures.jl Co-authored-by: Shuhei Kadowaki <[email protected]> * Update docstring * Tighten the type of `signatures` * Use MethodInfoKey from CodeTracking * Add CodeTracking as a direct dependency * Remove `method_table` definition We already used `extract_method_table` from JuliaInterpreter, obsoleting the introduction of `method_table`. * Don't accidentally override `Base.sin(::Float64)` * Temporarily add CodeTracking PR for CI testing * Fix patch for Documenter on CI * Add missing handling for methods defined for external method tables * Adjust docs, add type annotations * Update CI dependency patch * Test function definition, not method definition * Update CI docs dependency patch * Update src/codeedges.jl Co-authored-by: Shuhei Kadowaki <[email protected]> * Remove CI patches --------- Co-authored-by: Shuhei Kadowaki <[email protected]>
1 parent fee2336 commit 3b60b6a

File tree

7 files changed

+99
-57
lines changed

7 files changed

+99
-57
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ version = "3.4.2"
44
authors = ["Tim Holy <[email protected]>"]
55

66
[deps]
7+
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
78
Compiler = "807dbc54-b67e-4c79-8afb-eafe4df6f2e1"
89
JuliaInterpreter = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
910

1011
[compat]
12+
CodeTracking = "2"
1113
Compiler = "0.1"
1214
JuliaInterpreter = "0.10"
1315
julia = "1.10"

src/LoweredCodeUtils.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ module LoweredCodeUtils
99
# This somewhat unusual structure is in place to support
1010
# the VS Code extension integration.
1111

12+
using CodeTracking: MethodInfoKey
13+
1214
using JuliaInterpreter
1315
using JuliaInterpreter: SSAValue, SlotNumber, Frame, Interpreter, RecursiveInterpreter
1416
using JuliaInterpreter: codelocation, is_global_ref, is_global_ref_egal, is_quotenode_egal, is_return,
1517
lookup, lookup_return, linetable, moduleof, next_until!, nstatements, pc_expr,
16-
step_expr!, whichtt
18+
step_expr!, whichtt, extract_method_table
1719
using Compiler: Compiler as CC
1820

1921
include("packagedef.jl")

src/codeedges.jl

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -244,27 +244,24 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
244244
add_inner!(cl, icl, i)
245245
continue
246246
elseif isexpr(stmt, :method)
247-
if length(stmt.args) === 3 && (arg3 = stmt.args[3]; arg3 isa CodeInfo)
248-
icl = CodeLinks(cl.thismod, arg3)
249-
add_inner!(cl, icl, i)
250-
end
251-
name = stmt.args[1]
252-
if isa(name, GlobalRef) || isa(name, Symbol)
247+
if length(stmt.args) === 1
248+
# A function with no methods was defined. Associate its new binding to it.
249+
name = stmt.args[1]
253250
if isa(name, Symbol)
254251
name = GlobalRef(cl.thismod, name)
255252
end
256-
assign = get(cl.nameassigns, name, nothing)
257-
if assign === nothing
258-
cl.nameassigns[name] = assign = Int[]
253+
if !isa(name, GlobalRef)
254+
error("name ", typeof(name), " not recognized")
259255
end
256+
assign = get!(Vector{Int}, cl.nameassigns, name)
260257
push!(assign, i)
261258
targetstore = get!(Links, cl.namepreds, name)
262259
target = P(name, targetstore)
263260
add_links!(target, stmt, cl)
264-
elseif name in (nothing, false)
265-
else
266-
@show stmt
267-
error("name ", typeof(name), " not recognized")
261+
elseif length(stmt.args) === 3 && (arg3 = stmt.args[3]; arg3 isa CodeInfo) # method definition
262+
# A method was defined for an existing function.
263+
icl = CodeLinks(cl.thismod, arg3)
264+
add_inner!(cl, icl, i)
268265
end
269266
rhs = stmt
270267
target = P(SSAValue(i), cl.ssapreds[i])

src/packagedef.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Base.Experimental.@optlevel 1
22

3-
using Core: SimpleVector
3+
using Core: SimpleVector, MethodTable
44
using Core.IR: CodeInfo, GotoIfNot, GotoNode, IR, MethodInstance, ReturnNode
55
@static if isdefined(Core.IR, :EnterNode)
66
using Core.IR: EnterNode

src/signatures.jl

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ function signature(sigsv::SimpleVector)
2424
end
2525

2626
"""
27-
sigt, lastpc = signature([interp::Interpreter=RecursiveInterpreter()], frame::Frame, pc::Int)
27+
(mt, sigt), lastpc = signature([interp::Interpreter=RecursiveInterpreter()], frame::Frame, pc::Int)
2828
29-
Compute the signature-type `sigt` of a method whose definition in `frame` starts at `pc`.
29+
Compute the method table `mt` and signature-type `sigt` of a method whose definition in `frame` starts at `pc`.
3030
Generally, `pc` should point to the `Expr(:method, methname)` statement, in which case
3131
`lastpc` is the final statement number in `frame` that is part of the signature
3232
(i.e, the line above the 3-argument `:method` expression).
3333
Alternatively, `pc` can point to the 3-argument `:method` expression,
3434
as long as all the relevant SSAValues have been assigned.
3535
In this case, `lastpc == pc`.
3636
37-
If no 3-argument `:method` expression is found, `sigt` will be `nothing`.
37+
If no 3-argument `:method` expression is found, `nothing` will be returned in place of `(mt, sigt)`.
3838
"""
3939
function signature(interp::Interpreter, frame::Frame, @nospecialize(stmt), pc::Int)
4040
mod = moduleof(frame)
@@ -52,9 +52,10 @@ function signature(interp::Interpreter, frame::Frame, @nospecialize(stmt), pc::I
5252
stmt = pc_expr(frame, pc)
5353
end
5454
isa(stmt, Expr) || return nothing, pc
55+
mt = extract_method_table(frame, stmt)
5556
sigsv = lookup(interp, frame, stmt.args[2])::SimpleVector
5657
sigt = signature(sigsv)
57-
return sigt, lastpc
58+
return MethodInfoKey(mt, sigt), lastpc
5859
end
5960
signature(interp::Interpreter, frame::Frame, pc::Int) = signature(interp, frame, pc_expr(frame, pc), pc)
6061
signature(frame::Frame, pc::Int) = signature(RecursiveInterpreter(), frame, pc)
@@ -187,7 +188,9 @@ function identify_framemethod_calls(frame::Frame)
187188
end
188189
msrc = stmt.args[3]
189190
if msrc isa CodeInfo
190-
key = key::Union{GlobalRef,Bool,Nothing}
191+
# XXX: Properly support interpolated `Core.MethodTable`. This will require using
192+
# `stmt.args[2]` instead of `stmt.args[1]` to identify the parent function.
193+
isa(key, Union{GlobalRef,Bool,Nothing}) || continue
191194
for (j, mstmt) in enumerate(msrc.code)
192195
isa(mstmt, Expr) || continue
193196
jj = j
@@ -444,9 +447,9 @@ function get_running_name(interp::Interpreter, frame::Frame, pc::Int, name::Glob
444447
pctop -= 1
445448
stmt = pc_expr(frame, pctop)
446449
end # end fix
447-
sigtparent, lastpcparent = signature(interp, frame, pctop)
450+
(mt, sigtparent), lastpcparent = signature(interp, frame, pctop)
448451
sigtparent === nothing && return name, pc, lastpcparent
449-
methparent = whichtt(sigtparent)
452+
methparent = whichtt(sigtparent, mt)
450453
methparent === nothing && return name, pc, lastpcparent # caller isn't defined, no correction is needed
451454
if isgen
452455
cname = GlobalRef(moduleof(frame), nameof(methparent.generator.gen))
@@ -515,8 +518,8 @@ end
515518
"""
516519
ret = methoddef!([interp::Interpreter=RecursiveInterpreter()], signatures, frame; define=true)
517520
518-
Compute the signature of a method definition. `frame.pc` should point to a
519-
`:method` expression. Upon exit, the new signature will be added to `signatures`.
521+
Compute the method table/signature pair of a method definition. `frame.pc` should point to a
522+
`:method` expression. Upon exit, the new method table/signature pair will be added to `signatures`.
520523
521524
There are several possible return values:
522525
@@ -535,27 +538,27 @@ occurs for "empty method" expressions, e.g., `:(function foo end)`. `pc` will be
535538
By default the method will be defined (evaluated). You can prevent this by setting `define=false`.
536539
This is recommended if you are simply extracting signatures from code that has already been evaluated.
537540
"""
538-
function methoddef!(interp::Interpreter, signatures, frame::Frame, @nospecialize(stmt), pc::Int; define::Bool=true)
541+
function methoddef!(interp::Interpreter, signatures::Vector{MethodInfoKey}, frame::Frame, @nospecialize(stmt), pc::Int; define::Bool=true)
539542
framecode, pcin = frame.framecode, pc
540543
if ismethod3(stmt)
541544
pc3 = pc
542545
arg1 = stmt.args[1]
543-
sigt, pc = signature(interp, frame, stmt, pc)
544-
meth = whichtt(sigt)
546+
(mt, sigt), pc = signature(interp, frame, stmt, pc)
547+
meth = whichtt(sigt, mt)
545548
if isa(meth, Method) && (meth.sig <: sigt && sigt <: meth.sig)
546549
pc = define ? step_expr!(interp, frame, stmt, true) : next_or_nothing!(interp, frame)
547550
elseif define
548551
pc = step_expr!(interp, frame, stmt, true)
549-
meth = whichtt(sigt)
552+
meth = whichtt(sigt, mt)
550553
end
551554
if isa(meth, Method) && (meth.sig <: sigt && sigt <: meth.sig)
552-
push!(signatures, meth.sig)
555+
push!(signatures, mt => meth.sig)
553556
else
554-
if arg1 === false || arg1 === nothing
557+
if arg1 === false || arg1 === nothing || isa(mt, MethodTable)
555558
# If it's anonymous and not defined, define it
556559
pc = step_expr!(interp, frame, stmt, true)
557-
meth = whichtt(sigt)
558-
isa(meth, Method) && push!(signatures, meth.sig)
560+
meth = whichtt(sigt, mt)
561+
isa(meth, Method) && push!(signatures, mt => meth.sig)
559562
return pc, pc3
560563
else
561564
# guard against busted lookup, e.g., https://github.com/JuliaLang/julia/issues/31112
@@ -596,7 +599,7 @@ function methoddef!(interp::Interpreter, signatures, frame::Frame, @nospecialize
596599
end
597600
found || return nothing
598601
while true # methods containing inner methods may need multiple trips through this loop
599-
sigt, pc = signature(interp, frame, stmt, pc)
602+
(mt, sigt), pc = signature(interp, frame, stmt, pc)
600603
stmt = pc_expr(frame, pc)
601604
while !isexpr(stmt, :method, 3)
602605
pc = next_or_nothing(interp, frame, pc) # this should not check define, we've probably already done this once
@@ -611,15 +614,15 @@ function methoddef!(interp::Interpreter, signatures, frame::Frame, @nospecialize
611614
# signature of the active method. So let's get the active signature.
612615
frame.pc = pc
613616
pc = define ? step_expr!(interp, frame, stmt, true) : next_or_nothing!(interp, frame)
614-
meth = whichtt(sigt)
615-
isa(meth, Method) && push!(signatures, meth.sig) # inner methods are not visible
617+
meth = whichtt(sigt, mt)
618+
isa(meth, Method) && push!(signatures, mt => meth.sig) # inner methods are not visible
616619
name === name3 && return pc, pc3 # if this was an inner method we should keep going
617620
stmt = pc_expr(frame, pc) # there *should* be more statements in this frame
618621
end
619622
end
620-
methoddef!(interp::Interpreter, signatures, frame::Frame, pc::Int; define::Bool=true) =
623+
methoddef!(interp::Interpreter, signatures::Vector{MethodInfoKey}, frame::Frame, pc::Int; define::Bool=true) =
621624
methoddef!(interp, signatures, frame, pc_expr(frame, pc), pc; define)
622-
function methoddef!(interp::Interpreter, signatures, frame::Frame; define::Bool=true)
625+
function methoddef!(interp::Interpreter, signatures::Vector{MethodInfoKey}, frame::Frame; define::Bool=true)
623626
pc = frame.pc
624627
stmt = pc_expr(frame, pc)
625628
if !ismethod(stmt)
@@ -628,27 +631,27 @@ function methoddef!(interp::Interpreter, signatures, frame::Frame; define::Bool=
628631
pc === nothing && error("pc at end of frame without finding a method")
629632
methoddef!(interp, signatures, frame, pc; define)
630633
end
631-
methoddef!(signatures, frame::Frame, pc::Int; define::Bool=true) =
634+
methoddef!(signatures::Vector{MethodInfoKey}, frame::Frame, pc::Int; define::Bool=true) =
632635
methoddef!(RecursiveInterpreter(), signatures, frame, pc_expr(frame, pc), pc; define)
633-
methoddef!(signatures, frame::Frame; define::Bool=true) =
636+
methoddef!(signatures::Vector{MethodInfoKey}, frame::Frame; define::Bool=true) =
634637
methoddef!(RecursiveInterpreter(), signatures, frame; define)
635638

636-
function methoddefs!(interp::Interpreter, signatures, frame::Frame, pc::Int; define::Bool=true)
639+
function methoddefs!(interp::Interpreter, signatures::Vector{MethodInfoKey}, frame::Frame, pc::Int; define::Bool=true)
637640
ret = methoddef!(interp, signatures, frame, pc; define)
638641
pc = ret === nothing ? ret : ret[1]
639642
return _methoddefs!(interp, signatures, frame, pc; define)
640643
end
641-
function methoddefs!(interp::Interpreter, signatures, frame::Frame; define::Bool=true)
644+
function methoddefs!(interp::Interpreter, signatures::Vector{MethodInfoKey}, frame::Frame; define::Bool=true)
642645
ret = methoddef!(interp, signatures, frame; define)
643646
pc = ret === nothing ? ret : ret[1]
644647
return _methoddefs!(interp, signatures, frame, pc; define)
645648
end
646-
methoddefs!(signatures, frame::Frame, pc::Int; define::Bool=true) =
649+
methoddefs!(signatures::Vector{MethodInfoKey}, frame::Frame, pc::Int; define::Bool=true) =
647650
methoddefs!(RecursiveInterpreter(), signatures, frame, pc; define)
648-
methoddefs!(signatures, frame::Frame; define::Bool=true) =
651+
methoddefs!(signatures::Vector{MethodInfoKey}, frame::Frame; define::Bool=true) =
649652
methoddefs!(RecursiveInterpreter(), signatures, frame; define)
650653

651-
function _methoddefs!(interp::Interpreter, signatures, frame::Frame, pc::Int; define::Bool=define)
654+
function _methoddefs!(interp::Interpreter, signatures::Vector{MethodInfoKey}, frame::Frame, pc::Int; define::Bool=define)
652655
while pc !== nothing
653656
stmt = pc_expr(frame, pc)
654657
if !ismethod(stmt)

test/codeedges.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ module ModSelective end
349349
edges = CodeEdges(ModEval, src)
350350
lr = lines_required(GlobalRef(ModEval, :revise538), src, edges)
351351
selective_eval_fromstart!(Frame(ModEval, src), lr, #=istoplevel=#true)
352-
@test isdefined(ModEval, :revise538) && length(methods(ModEval.revise538, (Float32,))) == 1
352+
@test isdefined(ModEval, :revise538) && isempty(methods(ModEval.revise538)) # function is defined, method is not
353353

354354
# https://github.com/timholy/Revise.jl/issues/599
355355
thk = Meta.lower(Main, quote

0 commit comments

Comments
 (0)