257 lines
9.5 KiB
Julia
257 lines
9.5 KiB
Julia
using InteractiveUtils
|
|
|
|
const colorcycle = [:light_blue, :light_green, :light_yellow, :light_magenta]
|
|
|
|
function humansize(bytes::Integer)
|
|
units = ("B", "kB", "MB", "GB")
|
|
magnitude = floor(Int, log(1024, 1 + bytes))
|
|
if 10 < bytes < 10*1024^magnitude
|
|
round(bytes / 1024^magnitude, digits=1)
|
|
else
|
|
round(Int, bytes / 1024^magnitude)
|
|
end, units[1+magnitude]
|
|
end
|
|
|
|
function about(obj::Any)
|
|
print(Base.summary(obj))
|
|
size, units = humansize(Base.summarysize(obj))
|
|
if ismutable(obj)
|
|
print(" (mutable)")
|
|
end
|
|
print(", ", size, units, "\n\n")
|
|
printstyled("Fields:\n", bold=true)
|
|
memorylayout(obj)
|
|
end
|
|
|
|
function about(type::Type)
|
|
if isprimitivetype(type)
|
|
print("Primitive ")
|
|
elseif isconcretetype(type)
|
|
print("Concrete ")
|
|
if Base.datatype_haspadding(type)
|
|
printstyled("(padded) ", color=:light_black)
|
|
end
|
|
elseif isabstracttype(type)
|
|
print("Abstract ")
|
|
end
|
|
if Base.issingletontype(type)
|
|
print("singleton ")
|
|
end
|
|
print(Base.summary(type))
|
|
print(" defined in ")
|
|
printstyled(type.name.module, color=:light_red)
|
|
size, units = humansize(Base.summarysize(type))
|
|
selfsize, selfunits = try
|
|
st = humansize(sizeof(type))
|
|
print(", ", st...)
|
|
st
|
|
catch _; (0, "") end
|
|
if (size, units) != (selfsize, selfunits)
|
|
print(" referencing ", size, units)
|
|
end
|
|
println("\n ", join(string.(supertypes(type)), " \e[31m<:\e[m "))
|
|
if isstructtype(type) && fieldcount(type) > 0
|
|
println("\nStruct with \e[1m", fieldcount(type), "\e[m fields:")
|
|
if type isa DataType
|
|
for (i, (fname, ftype)) in enumerate(zip(fieldnames(type), fieldtypes(type)))
|
|
color = colorcycle[i % length(colorcycle) + 1]
|
|
print(" • ")
|
|
printstyled(fname; color)
|
|
printstyled("::", string(ftype), color=:light_black)
|
|
print('\n')
|
|
end
|
|
print('\n')
|
|
memorylayout(type)
|
|
else
|
|
println(" • ",
|
|
join(string.(fieldnames(type), "\e[90m::",
|
|
fieldtypes(type), "\e[m"),
|
|
"\n • "))
|
|
end
|
|
end
|
|
end
|
|
|
|
function structinfo(T::DataType)
|
|
map(1:fieldcount(T)) do i
|
|
size = Int(if i < fieldcount(T) fieldoffset(T, i+1)
|
|
else sizeof(T) end - fieldoffset(T, i))
|
|
contentsize=try sizeof(fieldtype(T, i)) catch _ 0 end
|
|
(; i, offset=fieldoffset(T, i) |> Int,
|
|
size, contentsize,
|
|
pointer=contentsize == 0,
|
|
name=fieldname(T, i),
|
|
type=fieldtype(T, i))
|
|
end
|
|
end
|
|
|
|
function memorylayout(type::DataType)
|
|
cpad(s, n::Integer, p=" ") = rpad(lpad(s,div(n+textwidth(s),2),p),n,p)
|
|
si = structinfo(type)
|
|
!isempty(si) || return
|
|
memstep = memstep = gcd((getfield.(si, :size), getfield.(si, :contentsize)) |>
|
|
Iterators.flatten |> collect)
|
|
memscale = max(1, floor(Int, 70/(sizeof(type)/memstep)))
|
|
print(' ')
|
|
for (; i, size, contentsize, pointer) in si
|
|
color = colorcycle[i % length(colorcycle) + 1]
|
|
contentwidth = memscale * contentsize÷memstep
|
|
printstyled('■'^contentwidth; color)
|
|
if contentsize < size
|
|
paddwidth = memscale * (size - contentsize)÷memstep
|
|
printstyled('■'^paddwidth;
|
|
color=if pointer; :cyan else :light_black end)
|
|
end
|
|
end
|
|
print("\n ")
|
|
for (; i, size, contentsize, pointer) in si
|
|
width = memscale * size÷memstep
|
|
color = colorcycle[i % length(colorcycle) + 1]
|
|
fsize, funits = humansize(size)
|
|
if pointer
|
|
printstyled(cpad("*", width); color)
|
|
elseif contentsize < size
|
|
csize, cunits = humansize(contentsize)
|
|
psize, punits = humansize(size - contentsize)
|
|
csizestr, psizestr = string(csize, cunits), string(psize, punits)
|
|
cwidth = div(width + textwidth(csizestr) - textwidth(psizestr) - 1, 2)
|
|
pwidth = width - cwidth
|
|
printstyled(lpad(csizestr, cwidth); color)
|
|
printstyled(rpad('+' * psizestr, pwidth); color=:light_black)
|
|
else
|
|
printstyled(cpad(string(fsize, funits), width); color)
|
|
end
|
|
end
|
|
if any(getfield.(si, :pointer))
|
|
printstyled("\n\n * = Pointer (8B)", color=:cyan)
|
|
end
|
|
print('\n')
|
|
end
|
|
|
|
function memorylayout(val::T) where {T}
|
|
si = structinfo(T)
|
|
!isempty(si) || return memorylayout(T)
|
|
rows = Vector{NamedTuple}()
|
|
for (; name, size, contentsize, pointer, type) in si
|
|
fsize, funits = humansize(size)
|
|
fbits = if isprimitivetype(type)
|
|
bitstring(getfield(val, name)) * '-'^(8*(size-contentsize))
|
|
elseif pointer
|
|
"Pointer"
|
|
else
|
|
""
|
|
end
|
|
fshow = sprint(show, getfield(val, name), context=:compact => true)
|
|
push!(rows, (; fname=string(name), fsize=string(fsize), funits,
|
|
fbits, fshow, ftype=string(type)))
|
|
end
|
|
widths = Dict(c => maximum(length.(getfield.(rows, c)))
|
|
for c in fieldnames(typeof(first(rows)))) |>
|
|
NamedTuple
|
|
for (i, (; fname, fsize, funits, fbits, fshow, ftype)) in enumerate(rows)
|
|
color = colorcycle[i % length(colorcycle) + 1]
|
|
printstyled(' ', lpad(fname, widths.fname), "::",
|
|
rpad(ftype, widths.ftype); color)
|
|
printstyled(" ", lpad(fsize, widths.fsize),
|
|
rpad(funits, widths.funits), ' '; color)
|
|
print(replace(rpad(fbits, widths.fbits), r"0|\-" => s"\e[90m\0\e[m"), ' ')
|
|
printstyled(rpad(fshow, widths.fshow); color)
|
|
print('\n')
|
|
end
|
|
print('\n')
|
|
memorylayout(T)
|
|
end
|
|
|
|
function about(fn::Function)
|
|
source = Main.InteractiveUtils.which(parentmodule(fn), Symbol(fn))
|
|
methodmodules = getproperty.(methods(fn).ms, :module)
|
|
others = setdiff(methodmodules, [source])
|
|
print(Base.summary(fn))
|
|
print("\n\nDefined in ")
|
|
printstyled(source, color=:light_red)
|
|
if length(others) > 0
|
|
printstyled("(", sum(Ref(source) .=== methodmodules), ")",
|
|
color=:light_black)
|
|
print(" extended in ")
|
|
for (i, oth) in enumerate(others)
|
|
printstyled(oth, color=:light_red)
|
|
printstyled("(", sum(Ref(oth) .=== methodmodules), ")",
|
|
color=:light_black)
|
|
if length(others) == 2 && i == 1
|
|
print(" and ")
|
|
elseif length(others) > 2 && i < length(others)-1
|
|
print(", ")
|
|
elseif length(others) > 2 && i == length(others)-1
|
|
print(", and ")
|
|
end
|
|
end
|
|
end
|
|
print(".\n")
|
|
end
|
|
|
|
function about(fn::Union{Function, Type}, typesig::Type{<:Tuple})
|
|
about(Base.unwrap_unionall(fn))
|
|
print("\n")
|
|
matchedmethods = methods(fn, typesig)
|
|
printstyled("Matching methods ($(length(matchedmethods.ms))):\n", bold=true)
|
|
for method in matchedmethods.ms
|
|
println(" ", sprint(show, method, context=IOContext(stdout)))
|
|
end
|
|
print('\n')
|
|
printstyled("Effects:\n", bold=true)
|
|
effects = Base.infer_effects(fn, typesig)
|
|
trichar(t::UInt8) =
|
|
Dict(Core.Compiler.ALWAYS_TRUE => '✔',
|
|
# Core.Compiler.TRISTATE_UNKNOWN => '?',
|
|
Core.Compiler.ALWAYS_FALSE => '✗')[t]
|
|
trichar(b::Bool) = ifelse(b, '✔', '✗')
|
|
tricolor(t::UInt8) =
|
|
Dict(Core.Compiler.ALWAYS_TRUE => :green,
|
|
# Core.Compiler.TRISTATE_UNKNOWN => :yellow,
|
|
Core.Compiler.ALWAYS_FALSE => :red)[t]
|
|
tricolor(b::Bool) = ifelse(b, :green, :red)
|
|
hedge(t::UInt8) =
|
|
Dict(Core.Compiler.ALWAYS_TRUE => "guaranteed to",
|
|
# Core.Compiler.TRISTATE_UNKNOWN => "might",
|
|
Core.Compiler.ALWAYS_FALSE => "not guaranteed to")[t]
|
|
hedge(b::Bool) = ifelse(b, "guaranteed to", "not guaranteed to")
|
|
for (effect, description) in
|
|
[(:consistent, "return or terminate consistently"),
|
|
(:effect_free, "be free from externally semantically visible side effects"),
|
|
(:nothrow, "never throw an exception"),
|
|
(:terminates, "terminate")]
|
|
e = getfield(effects, effect)
|
|
print(" "); printstyled(trichar(e), ' ', string(effect), color=tricolor(e))
|
|
print(" "); printstyled(hedge(e), ' ', description, color=:light_black);
|
|
print('\n')
|
|
end
|
|
if !effects.nonoverlayed
|
|
print(" "); printstyled(trichar(Core.Compiler.ALWAYS_FALSE), " nonoverlayed",
|
|
color=tricolor(Core.Compiler.ALWAYS_FALSE))
|
|
print(" "); printstyled("methods within this method are not defined in an overlayed method table",
|
|
color=:light_black);
|
|
print('\n')
|
|
end
|
|
end
|
|
|
|
macro about(obj::Symbol, types::Union{Symbol, Expr}...)
|
|
if Core.eval(Main, obj) isa Function && length(types) == 1 &&
|
|
types[1] == :(())
|
|
:(about($obj, Tuple{}))
|
|
elseif Core.eval(Main, obj) isa Function && length(types) > 0
|
|
:(about($obj, $(Tuple{Core.eval.(Ref(Main), types)...})))
|
|
else
|
|
:(about($obj))
|
|
end |> esc
|
|
end
|
|
|
|
macro about(qobj::Union{QuoteNode, Expr}, types::Union{Symbol, Expr}...)
|
|
if Core.eval(Main, Core.eval(Main, qobj)) isa Function && length(types) == 1 &&
|
|
types[1] == :(())
|
|
:(about($(Core.eval(Main, qobj)), Tuple{}))
|
|
elseif length(types) > 0
|
|
:(about($(Core.eval(Main, qobj)), $(Tuple{Core.eval.(Ref(Main), types)...})))
|
|
else
|
|
:(about($(Core.eval(Main, qobj))))
|
|
end |> esc
|
|
end
|