Skip to content

Commit e89f508

Browse files
authored
Correctly revise methods defined in external method tables (#904)
* Revise external method tables * Bump compat for CodeTracking * Use MethodInfoKey, rename a few `sigs` to `mt_sigs` * Start updating docs a bit * Patch CI to test with upstream dependencies * Update outdated comment * Attempt to fix Git tests * Refactor tests and increase coverage * Add tests for method deletion * Bump JuliaInterpreter compat bound * Fix tests for 1.10 * Update documentation, more `mt_sigs` renames * Add NEWS.md entry * Bump package version to 3.8.0 * Update CI patches * Update CI dependency patch * Don't test `(::Method).deleted_world` on 1.12+ * Remove CI patches * Raise compat bound for LoweredCodeUtils
1 parent a749d1f commit e89f508

File tree

10 files changed

+212
-89
lines changed

10 files changed

+212
-89
lines changed

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
This file describes only major changes, and does not include bug fixes,
44
cleanups, or minor enhancements.
55

6+
## Revise 3.8
7+
8+
* Methods defined on external method tables via `Base.Experimental.@overlay` can now be correctly revised ([#904]).
9+
610
## Revise 3.3
711

812
* Upgrade to JuliaInterpreter 0.9 and drop support for Julia prior to 1.6 (the new LTS).
@@ -151,3 +155,4 @@ New features:
151155
[#243]: https://github.com/timholy/Revise.jl/pull/243
152156
[#245]: https://github.com/timholy/Revise.jl/pull/245
153157
[#278]: https://github.com/timholy/Revise.jl/pull/278
158+
[#904]: https://github.com/timholy/Revise.jl/pull/904

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
2121
DistributedExt = "Distributed"
2222

2323
[compat]
24-
CodeTracking = "1.2"
24+
CodeTracking = "2"
2525
Distributed = "1"
2626
JuliaInterpreter = "0.10"
27-
LoweredCodeUtils = "3.3"
27+
LoweredCodeUtils = "3.4.3"
2828
OrderedCollections = "1"
2929
# Exclude Requires-1.1.0 - see https://github.com/JuliaPackaging/Requires.jl/issues/94
3030
Requires = "~1.0, ^1.1.1"

docs/src/debugging.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,13 @@ julia> rlogger.logs
159159
#= /tmp/revisetest.jl:9 =#
160160
x ^ 4
161161
end))))
162-
Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459331061e9, deltainfo=(Any[Tuple{typeof(mult2),Any}], :(#= /tmp/revisetest.jl:11 =#) => :(#= /tmp/revisetest.jl:13 =#))))
162+
Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459331061e9, deltainfo=(Pair{Union{Nothing, MethodTable}, Type}[nothing => Tuple{typeof(mult2),Any}], :(#= /tmp/revisetest.jl:11 =#) => :(#= /tmp/revisetest.jl:13 =#))))
163163
Revise.LogRecord(Debug, Eval, Action, Revise_9147188b, "/home/tim/.julia/dev/Revise/src/Revise.jl", 276, (time=1.557996459391182e9, deltainfo=(Main.ReviseTest.Internal, :(mult3(x) = begin
164164
#= /tmp/revisetest.jl:14 =#
165165
3x
166166
end))))
167-
Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459391642e9, deltainfo=(Any[Tuple{typeof(unchanged),Any}], :(#= /tmp/revisetest.jl:18 =#) => :(#= /tmp/revisetest.jl:19 =#))))
168-
Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459391695e9, deltainfo=(Any[Tuple{typeof(unchanged2),Any}], :(#= /tmp/revisetest.jl:20 =#) => :(#= /tmp/revisetest.jl:21 =#))))
167+
Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459391642e9, deltainfo=(Pair{Union{Nothing, MethodTable}, Type}[nothing => Tuple{typeof(unchanged),Any}], :(#= /tmp/revisetest.jl:18 =#) => :(#= /tmp/revisetest.jl:19 =#))))
168+
Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459391695e9, deltainfo=(Pair{Union{Nothing, MethodTable}, Type}[nothing => Tuple{typeof(unchanged2),Any}], :(#= /tmp/revisetest.jl:20 =#) => :(#= /tmp/revisetest.jl:21 =#))))
169169
```
170170

171171
You can see that Revise started by deleting three methods, followed by evaluating three new versions of those methods. Interspersed are various changes to the line numbering.

docs/src/internals.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ Tuple{typeof(print_item),IO,Any,Integer,String} # print_item(io, item, 2, "
137137
```
138138

139139
In Revise's internal code, a definition is often represented with a variable `def`, and a signature-type with `sigt`.
140+
The method table for which the method was defined is also represented, to form a `mt_sigt` pair.
140141
Recent versions of Revise do not make extensive use of signature expressions.
141142

142143
### Computing signatures
@@ -226,9 +227,9 @@ Most of Revise's magic comes down to just three internal variables:
226227

227228
Two "maps" are central to Revise's inner workings: `ExprsSigs` maps link
228229
definition=>signature-types (the forward workflow), while `CodeTracking` (specifically,
229-
its internal variable `method_info`) links from
230-
signature-type=>definition (the backward workflow).
231-
Concretely, `CodeTracking.method_info` is just an `IdDict` mapping `sigt=>(locationinfo, def)`.
230+
its internal variable `method_info`) links from a
231+
method table/signature-type pair to the corresponding definition (the backward workflow).
232+
Concretely, `CodeTracking.method_info` is just an `IdDict` mapping `(mt => sigt) => (locationinfo, def)`.
232233
Of note, a stack frame typically contains a link to a method, which stores the equivalent
233234
of `sigt`; consequently, this information allows one to look up the corresponding
234235
`locationinfo` and `def`. (When methods move, the location information stored by CodeTracking
@@ -237,8 +238,8 @@ gets updated by Revise.)
237238
Some additional notes about Revise's `ExprsSigs` maps:
238239

239240
- For expressions that do not define a method, it is just `def=>nothing`
240-
- For expressions that do define a method, it is `def=>[sigt1, ...]`.
241-
`[sigt1, ...]` is the list of signature-types generated from `def` (often just one,
241+
- For expressions that do define a method, it is `def=>[mt_sigt1, ...]`.
242+
`[mt_sigt1, ...]` is the list of method table/signature-type pairs generated from `def` (often just one,
242243
but more in the case of methods with default arguments or keyword arguments).
243244
- They are represented as an `OrderedDict` so as to preserve the sequence in which expressions
244245
occur in the file.
@@ -255,7 +256,7 @@ Some additional notes about Revise's `ExprsSigs` maps:
255256
the location information stored by `CodeTracking`.
256257

257258
`ExprsSigs` are organized by module and then file, so that one can map
258-
`filename`=>`module`=>`def`=>`sigts`.
259+
`filename`=>`module`=>`def`=>`mt_sigts`.
259260
Importantly, single-file modules can be "reconstructed" from the keys of the corresponding
260261
`ExprsSigs` (and multi-file modules from a collection of such items), since they hold
261262
the complete ordered set of expressions that would be `eval`ed to define the module.
@@ -334,13 +335,13 @@ FileInfo(Items=>ExprsSigs with the following expressions:
334335
end), )
335336
```
336337
337-
This is just a summary; to see the actual `def=>sigts` map, do the following:
338+
This is just a summary; to see the actual `def=>mt_sigts` map, do the following:
338339
339340
```julia
340341
julia> pkgdata.fileinfos[2].modexsigs[Items]
341-
OrderedCollections.OrderedDict{Revise.RelocatableExpr,Union{Nothing, Array{Any,1}}} with 2 entries:
342-
:(indent(::UInt16) = begin => Any[Tuple{typeof(indent),UInt16}]
343-
:(indent(::UInt8) = begin => Any[Tuple{typeof(indent),UInt8}]
342+
OrderedCollections.OrderedDict{Module, OrderedCollections.OrderedDict{Revise.RelocatableExpr, Union{Nothing, Vector{Pair{Union{Nothing, Core.MethodTable}, Type}}}}} with 2 entries:
343+
:(indent(::UInt16) = begin => Pair{Union{Nothing, MethodTable}, Type}[nothing => Tuple{typeof(indent),UInt16}]
344+
:(indent(::UInt8) = begin => Pair{Union{Nothing, MethodTable}, Type}[nothing => Tuple{typeof(indent),UInt8}]
344345
```
345346
346347
These are populated now because we specified `__precompile__(false)`, which forces

src/Revise.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module Revise
3535

3636
using OrderedCollections, CodeTracking, JuliaInterpreter, LoweredCodeUtils
3737

38-
using CodeTracking: PkgFiles, basedir, srcfiles, basepath
38+
using CodeTracking: PkgFiles, basedir, srcfiles, basepath, MethodInfoKey
3939
using JuliaInterpreter: Compiled, Frame, Interpreter, LineTypes, RecursiveInterpreter
4040
using JuliaInterpreter: codelocs, finish_and_return!, get_return, is_doc_expr, isassign,
4141
isidentical, is_quotenode_egal, linetable, lookup, moduleof,

src/lowered.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ end
99
# This defines the API needed to store signatures using methods_by_execution!
1010
# This default version is simple and only used for testing purposes.
1111
# The "real" one is CodeTrackingMethodInfo in Revise.jl.
12-
const MethodInfo = IdDict{Type,LineNumberNode}
12+
const MethodInfo = IdDict{MethodInfoKey,LineNumberNode}
1313
add_signature!(methodinfo, @nospecialize(sig), ln) = push!(methodinfo, sig=>ln)
1414
push_expr!(methodinfo, mod::Module, ex::Expr) = methodinfo
1515
pop_expr!(methodinfo) = methodinfo
@@ -309,7 +309,7 @@ function _methods_by_execution!(interp::Interpreter, methodinfo, frame::Frame, i
309309
mod = moduleof(frame)
310310
# Hoist this lookup for performance. Don't throw even when `mod` is a baremodule:
311311
modinclude = isdefined(mod, :include) ? getfield(mod, :include) : nothing
312-
signatures = [] # temporary for method signature storage
312+
signatures = MethodInfoKey[] # temporary for method signature storage
313313
pc = frame.pc
314314
while true
315315
JuliaInterpreter.is_leaf(frame) || (@warn("not a leaf"); break)
@@ -457,13 +457,13 @@ function _methods_by_execution!(interp::Interpreter, methodinfo, frame::Frame, i
457457
uT = Base.unwrap_unionall(T)::DataType
458458
ft = uT.types
459459
sig1 = Tuple{Base.rewrap_unionall(Type{uT}, T), Any[Any for i in 1:length(ft)]...}
460-
push!(signatures, sig1)
460+
push!(signatures, nothing => sig1)
461461
sig2 = Base.rewrap_unionall(Tuple{Type{T}, ft...}, T)
462462
while T isa UnionAll
463463
sig2 isa UnionAll || (sig2 = sig1; break) # sig2 doesn't define all parameters, so drop it
464464
T = T.body
465465
end
466-
sig1 == sig2 || push!(signatures, sig2)
466+
sig1 == sig2 || push!(signatures, nothing => sig2)
467467
for sig in signatures
468468
add_signature!(methodinfo, sig, lnn)
469469
end

0 commit comments

Comments
 (0)