Last active
January 31, 2023 21:04
-
-
Save staticfloat/73e58d831dfbc733380d448f14f946e5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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