Skip to content

Commit 606e0a1

Browse files
committed
add a vibe coded script that checks for closures boxes in loaded code
1 parent b6337dd commit 606e0a1

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

contrib/scan-closure-boxes.jl

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
# NOTE: This script is mostly AI-generated
3+
4+
function is_box_call(expr)
5+
if !(expr isa Expr)
6+
return false
7+
end
8+
if expr.head === :call
9+
callee = expr.args[1]
10+
return callee === Core.Box || (callee isa GlobalRef && callee.mod === Core && callee.name === :Box)
11+
elseif expr.head === :new
12+
callee = expr.args[1]
13+
return callee === Core.Box || (callee isa GlobalRef && callee.mod === Core && callee.name === :Box)
14+
end
15+
return false
16+
end
17+
18+
function slot_name(ci, slot)
19+
if slot isa Core.SlotNumber
20+
idx = Int(slot.id)
21+
if 1 <= idx <= length(ci.slotnames)
22+
return string(ci.slotnames[idx])
23+
end
24+
end
25+
return string(slot)
26+
end
27+
28+
function method_location(m::Method)
29+
file = m.file
30+
line = m.line
31+
file_str = file isa Symbol ? String(file) : string(file)
32+
if file_str == "none" || line == 0
33+
return ("", 0)
34+
end
35+
return (file_str, line)
36+
end
37+
38+
function root_module(mod::Module)
39+
while true
40+
parent = parentmodule(mod)
41+
if parent === mod || parent === Main || parent === Core
42+
return mod
43+
end
44+
mod = parent
45+
end
46+
end
47+
48+
function format_box_log(var, m::Method)
49+
file, line = method_location(m)
50+
return string(
51+
"mod=", root_module(m.module),
52+
"\tvar=", var,
53+
"\tfunc=", m.name,
54+
"\tsig=", m.sig,
55+
"\tfile=", file,
56+
"\tline=", line
57+
)
58+
end
59+
60+
function format_box_fields(var, m::Method)
61+
file, line = method_location(m)
62+
return (
63+
mod = string(root_module(m.module)),
64+
var = string(var),
65+
func = string(m.name),
66+
sig = string(m.sig),
67+
file = string(file),
68+
line = string(line),
69+
)
70+
end
71+
72+
function escape_md(s)
73+
return replace(string(s), "|" => "\\|")
74+
end
75+
76+
function md_code(s)
77+
return "`" * replace(string(s), "`" => "``") * "`"
78+
end
79+
80+
function scan_method!(lines, m::Method, modules)
81+
root = string(root_module(m.module))
82+
if !isempty(modules) && !(root in modules)
83+
return
84+
end
85+
ci = try
86+
Base.uncompressed_ast(m)
87+
catch
88+
return
89+
end
90+
for stmt in ci.code
91+
if stmt isa Expr && stmt.head === :(=)
92+
lhs = stmt.args[1]
93+
rhs = stmt.args[2]
94+
if is_box_call(rhs)
95+
push!(lines, format_box_fields(slot_name(ci, lhs), m))
96+
end
97+
elseif is_box_call(stmt)
98+
push!(lines, format_box_fields("<unknown>", m))
99+
end
100+
end
101+
end
102+
103+
function print_help()
104+
println("scan-closure-boxes.jl: scan loaded methods for Core.Box allocations")
105+
println()
106+
println("usage:")
107+
println(" scan-closure-boxes.jl [--module=Base,Core,...|stdlibs] [--format=plain|markdown]")
108+
println()
109+
println("options:")
110+
println(" --module=... Comma-separated root modules to include; 'stdlibs' expands to all stdlibs.")
111+
println(" --format=... Output format: plain or markdown (tables).")
112+
end
113+
114+
function parse_args(args)
115+
modules = String[]
116+
format = "plain"
117+
for arg in args
118+
if arg == "--help" || arg == "-h"
119+
print_help()
120+
exit(0)
121+
elseif startswith(arg, "--module=")
122+
modlist = split(arg[(length("--module=") + 1):end], ',')
123+
for mod in modlist
124+
isempty(mod) || push!(modules, mod)
125+
end
126+
elseif startswith(arg, "--format=")
127+
format = arg[(length("--format=") + 1):end]
128+
if !(format in ("plain", "markdown", "markdown-table"))
129+
error("unknown format: " * format)
130+
end
131+
else
132+
error("unknown argument: " * arg)
133+
end
134+
end
135+
return Set(modules), format
136+
end
137+
138+
function stdlib_modules()
139+
stdlib_dir = Sys.STDLIB
140+
mods = String[]
141+
for entry in readdir(stdlib_dir)
142+
path = joinpath(stdlib_dir, entry)
143+
if isdir(path) && isfile(joinpath(path, "src", entry * ".jl"))
144+
push!(mods, entry)
145+
end
146+
end
147+
return Set(mods)
148+
end
149+
150+
function load_requested_modules(modules)
151+
for name in modules
152+
name == "Base" && continue
153+
name == "Core" && continue
154+
name == "Main" && continue
155+
try
156+
Base.require(Main, Symbol(name))
157+
catch err
158+
@warn "failed to load module" mod=name err=err
159+
end
160+
end
161+
end
162+
163+
function scan_all_methods()
164+
modules, format = parse_args(ARGS)
165+
if "stdlibs" in modules
166+
delete!(modules, "stdlibs")
167+
union!(modules, stdlib_modules())
168+
end
169+
load_requested_modules(modules)
170+
lines = Vector{NamedTuple}()
171+
Base.visit(Core.methodtable) do m
172+
scan_method!(lines, m, modules)
173+
end
174+
sort!(lines, by = entry -> entry.mod)
175+
if format == "plain"
176+
for entry in lines
177+
println("mod=", entry.mod,
178+
"\tvar=", entry.var,
179+
"\tfunc=", entry.func,
180+
"\tsig=", entry.sig,
181+
"\tfile=", entry.file,
182+
"\tline=", entry.line)
183+
end
184+
else
185+
# treat "markdown" and "markdown-table" as table output
186+
last_mod = ""
187+
for entry in lines
188+
if entry.mod != last_mod
189+
if !isempty(last_mod)
190+
println()
191+
end
192+
println("## ", entry.mod)
193+
println("| var | func | sig | file | line |")
194+
println("| --- | --- | --- | --- | --- |")
195+
last_mod = entry.mod
196+
end
197+
println("| ", md_code(escape_md(entry.var)),
198+
" | ", md_code(escape_md(entry.func)),
199+
" | ", md_code(escape_md(entry.sig)),
200+
" | ", md_code(escape_md(entry.file)),
201+
" | ", md_code(escape_md(entry.line)),
202+
" |")
203+
end
204+
end
205+
return nothing
206+
end
207+
208+
scan_all_methods()

0 commit comments

Comments
 (0)