Two more holes of the arena's family, found from ECS's births: - a function called through a function value (UiClass.make's cmp_x_new, a step list, a System's tick) has its result flow nowhere the analysis can see, so what it returned looked LOCAL - and a caller keeping it (ludic.ui's instance table) would keep scratch. Every function taken as a value (fn f) now has its result kept. - an entry block has no name, and the analysis only walked named declarations: what an entry stored was never seen. It is walked now. examples/lang/arena_fnval.ludic (a factory in a field, its records kept by a pool across frames) crashed poisoned before and prints '5 1053' as the heap does now; in ludic-dev test. Maroon Lake: component constructors are kept, not births (birth_leaks 115 -> 103); what fn values return is kept (frame_allocs 194 -> 210, frame_keeps 151 -> 162); 5293 sites local, 6586 kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
25 lines
987 B
Text
25 lines
987 B
Text
# arena_fnval.ludic - a record made by a function called through a function value (a factory in a
|
|
# field, as ludic.ui's UiClass.make is) and kept by a pool across frames. The analysis cannot see
|
|
# that call, so a function taken as a value keeps what it returns: built with --arena and poisoned on
|
|
# every reset it prints `5 1053`, as the heap does.
|
|
program ArenaFnval {
|
|
property Item { v: int = 0, name: string = "" }
|
|
property Maker { make: fn(int) -> Item = null }
|
|
property Pool { items: []Item = new []Item }
|
|
function item_new(f: int) -> Item { return new Item { v: f, name: `item {f}` } }
|
|
function step(p: Pool, m: Maker, f: int) -> void {
|
|
if f % 100 == 3 { push(p.items, m.make(f)) }
|
|
}
|
|
entry {
|
|
let p = new Pool
|
|
let m = new Maker
|
|
m.make = fn item_new
|
|
for f in 0 .. 500 {
|
|
step(p, m, f)
|
|
Mem.frame()
|
|
}
|
|
var sum = 0
|
|
for i in 0 .. len(p.items) { sum += p.items[i].v + len(p.items[i].name) }
|
|
print(`{len(p.items)} {sum}`)
|
|
}
|
|
}
|