@@ -8,6 +8,7 @@ using Base.Meta: isexpr
88
99export visit, call_type, methodinstance, methodinstances, worlds # findcallers is exported from its own file
1010export visit_backedges, all_backedges, with_all_backedges, terminal_backedges, direct_backedges
11+ export child_modules, methodinstances_owned_by
1112export hasbox
1213
1314include (" visit.jl" )
@@ -150,7 +151,8 @@ julia> methodinstances(m)
150151```
151152
152153Note the method `m` was broader than the signature we queried with, and the returned `MethodInstance`s reflect that breadth.
153- See [`methodinstances`](@ref) for a more restrictive subset.
154+ See [`methodinstances`](@ref) for a more restrictive subset, and [`methodinstances_owned_by`](@ref) for collecting
155+ MethodInstances owned by specific modules.
154156"""
155157function methodinstances (top= ())
156158 if isa (top, Module) || isa (top, Function) || isa (top, Type) || isa (top, Method) || isa (top, Base. MethodList)
@@ -190,6 +192,96 @@ function methodinstances(@nospecialize(types::Type))
190192 return methodinstances (f, types)
191193end
192194
195+ """
196+ mods = child_modules(mod::Module; external::Bool=false)
197+
198+ Return a list that includes `mod` and all sub-modules of `mod`.
199+ By default, modules loaded from other sources (e.g., packages or those
200+ defined by Julia itself) are excluded, even if exported (or `@reexport`ed,
201+ see https://github.com/simonster/Reexport.jl), unless you set `external=true`.
202+
203+ # Examples
204+
205+ ```jldoctest
206+ julia> module Outer
207+ module Inner
208+ export Base
209+ end
210+ end
211+ Main.Outer
212+
213+ julia> child_modules(Outer)
214+ 2-element Vector{Module}:
215+ Main.Outer
216+ Main.Outer.Inner
217+
218+ julia> child_modules(Outer.Inner)
219+ 1-element Vector{Module}:
220+ Main.Outer.Inner
221+ ```
222+
223+ # Extended help
224+
225+ In the example above, because of the `export Base`, the following `visit`-based implementation would
226+ also collect `Base` and all of its sub-modules:
227+
228+ ```jldoctest
229+ julia> mods = Module[]
230+ Module[]
231+
232+ julia> visit(Outer) do item
233+ if item isa Module
234+ push!(mods, item)
235+ return true
236+ end
237+ return false
238+ end
239+
240+ julia> Base ∈ mods
241+ true
242+
243+ julia> length(mods) > 20
244+ true
245+ ```
246+ """
247+ function child_modules (mod:: Module ; external:: Bool = false )
248+ function rootmodule (m:: Module )
249+ m == mod && return m # anything under `mod` has a root of `mod`
250+ pm = parentmodule (m)
251+ m == pm && return m
252+ return rootmodule (pm)
253+ end
254+ mods = Module[]
255+ visit (mod) do item
256+ if item isa Module && (external || rootmodule (item) == mod)
257+ push! (mods, item)
258+ return true
259+ end
260+ return false # don't recurse into Methods, MethodTables, MethodInstances, etc.
261+ end
262+ return mods
263+ end
264+
265+ """
266+ mis = methodinstances_owned_by(mod::Module; include_child_modules::Bool=true, kwargs...)
267+
268+ Return a list of `MethodInstance`s that are owned by `mod`. If `include_child_modules` is `true`,
269+ this includes sub-modules of `mod`, in which case `kwargs` are passed to [`child_modules`](@ref).
270+
271+ The primary difference between `methodinstances(mod)` and `methodinstances_owned_by(mod)` is that
272+ the latter excludes `MethodInstances` that belong to re-exported dependent packages.
273+ """
274+ function methodinstances_owned_by (mod:: Module ; include_child_modules:: Bool = true , kwargs... )
275+ mods = include_child_modules ? child_modules (mod; kwargs... ) : [mod]
276+ # get all MethodInstances owned by one of the modules in `mods`
277+ # these are the only MethodInstances that can be precompiled in current versions of Julia
278+ return filter (methodinstances (mod)) do mi
279+ m = mi. def
280+ m isa Method && return m. module ∈ mods
281+ return m ∈ mods
282+ end
283+ end
284+
193285if isdefined (Base, :code_typed_by_type )
194286 function hasbox (mi:: MethodInstance )
195287 try
0 commit comments