A load freed only the parsed trip's nodes (Json.free) and kept every string the parser made, because
a loader might keep one; free_all freed every string and every key, so on a tree a migration had
added a literal to (`Value.put(v, "sver", Value.str("2"))`) it freed the literal and aborted.
- The parser marks the string values it makes (JP_OWNED, in the node's otherwise unused num) and
interns object keys (a few names, never freed); free_all frees only marked strings, never keys,
and a list's spares too. A setter that gives a marked node other text (value_set_str/_strs,
value_into_str/_strs, value_become) frees the parser's text first. value_as_int / _as_float read a
string as 0 as before.
- ludic.base sv_str returns intern(...): every package load that keeps a text read from a section
(minimap labels, a Thing's look, photo tags and files, an effect's label, ...) holds its own copy.
Golden json_free_edited: parse, put a literal key and string in, set a string, keep an interned copy,
free_all - 1100 loads: the copy reads on and nothing grows (the toolchain before this aborts, 134).
Tests: ludic.save, base, settings, minimap, things, photo, effects; json_saves, value_list_regrow; the
36 ui examples.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
24 lines
932 B
Text
24 lines
932 B
Text
# json_free_edited.ludic - a parsed file let go whole (Json.free_all) after it was migrated and read:
|
|
# a literal key and a literal string put in, a string set in place, a copy kept - only what the
|
|
# parser made is freed, so nothing crashes, the copy reads on, and a thousand loads keep nothing:
|
|
# names Sam|Ada grew 32
|
|
program JsonFreeEdited {
|
|
function read_one(text: string, keep: []string) -> void {
|
|
let v = Json.parse(text)
|
|
Value.put(v, "sver", Value.str("2"))
|
|
value_set_str(v, "name", "Ada")
|
|
if len(keep) < 2 { push(keep, intern(Value.as_str(Value.get(v, "who")))) }
|
|
Json.free_all(v)
|
|
}
|
|
entry {
|
|
let keep = new []string
|
|
let text = "{\"name\": \"Sam\", \"who\": \"Sam\", \"pins\": [\"a\", \"b\"], \"n\": 3}"
|
|
read_one(text, keep)
|
|
push(keep, "Ada")
|
|
for f in 0 .. 1100 {
|
|
read_one(text, keep)
|
|
Mem.frame()
|
|
}
|
|
print(`names {keep[0]}|{keep[1]} grew {Mem.kept()}`)
|
|
}
|
|
}
|