Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@staticfloat
Last active January 31, 2023 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save staticfloat/73e58d831dfbc733380d448f14f946e5 to your computer and use it in GitHub Desktop.
Save staticfloat/73e58d831dfbc733380d448f14f946e5 to your computer and use it in GitHub Desktop.
using REPL
# Helper function to determine if something is "struct"-like
is_struct_like(x) = Base.nfields(x) > 0
function is_array_like(x)
return typeof(x) <: AbstractArray
end
# We sadly can't use `∉` because `missing ∉ haystack` is `missing`, not `false` :(
function missing_resistant_contains(needle, haystack)
return any(needle === hay for hay in haystack)
end
# Restrict our search to only values that exist within the top-level module we provided for the search
function is_parent_of(root_module::Module, mod::Module)
last_mod = nothing
while last_mod != mod
last_mod = mod
mod = parentmodule(mod)
if mod == root_module
return true
end
end
return false
end
# Helper functions to skip compiler internals that are unlikely to
is_compiler_internal(x) = false
is_compiler_internal(x::Core.MethodTable) = true
function objwalk(f, p, VISITED=Base.IdSet{Any}(), path = string(p), root_module = p;
skip_compiler_internals::Bool = true,
include_only_direct_children::Bool = true)
if missing_resistant_contains(p, VISITED)
return
end
push!(VISITED, p)
# If `p` is a module, iterate over all names within it
if isa(p, Module)
# If we're only including direct children, skip this module if it's not parented by the root module
if include_only_direct_children && !is_parent_of(root_module, p)
return
end
for name in names(p; all=true, imported=true)
if isdefined(p, name)
p_sub = getproperty(p, name)
objwalk(f, p_sub, VISITED, string(path, ".", name), root_module; skip_compiler_internals)
end
end
# Also interrogate modules used but not defined by this module
for mod in REPL.moduleusings(p)
objwalk(f, mod, VISITED, string(path, ".", string(mod)), root_module; skip_compiler_internals, include_only_direct_children)
end
# If `p` is a `struct` type, iterate over all fields
elseif is_struct_like(p)
# If this is a compiler internals struct (like a `MethodTable` or similar)
# we can typically skip it without fear.
if skip_compiler_internals && is_compiler_internal(p)
return
end
for field_idx in 1:Base.fieldcount(typeof(p))
if isdefined(p, field_idx)
p_idx = Base.getfield(p, field_idx)
objwalk(f, p_idx, VISITED, string(path, ".", Base.fieldname(typeof(p), field_idx)), root_module; skip_compiler_internals, include_only_direct_children)
end
end
# If `p` is array-like, iterate over it!
elseif is_array_like(p)
#@info("Array-like!", p)
for iter_idx in 1:length(p)
if isassigned(p, iter_idx)
objwalk(f, p[iter_idx], VISITED, string(path, "[", iter_idx, "]"), root_module; skip_compiler_internals, include_only_direct_children)
end
end
else
f(p, path)
end
end
#=
Example usage:
using Gtk
objwalk(Gtk; include_only_direct_children=false) do p, path
if (isa(p, AbstractString) && occursin("libgtk", p)) || occursin("libgtk", path)
@info(path, p)
end
end
=#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment