Summary
Building a JSON tree with the creation API (json.obj() / json.arr()) and
freeing the root with json.json_free(root) leaks the nested created objects/
arrays. json_free does not fully reclaim children attached via json.set /
json.push on the builder (non-parsed) path.
Minimal repro
import std.json
main() {
root = json.obj()
props = json.obj()
p = json.obj()
_ = json.set(p, "type", json.str("integer"))
_ = json.set(props, "age", p)
_ = json.set(root, "properties", props)
out, _ = json.encode(root)
print("${out}\n")
json.json_free(root)
}
$ valgrind --leak-check=full ./repro
definitely lost: 64 bytes in 2 blocks # props + p, the two nested objects
The two nested json_create_object allocations (props, p) are lost. A single
top-level object frees fine; only nested-attached children leak.
Root cause (likely)
json_free (std/json/aether_json.c) frees via an arena when one is attached,
else heap_free_tree. Objects made with the creation API are heap_new'd with
JV_FLAG_HEAP_STRUCT; children attached later via object_set/array_add don't
appear to be linked into the parent's free path the way parsed-tree children are,
so heap_free_tree(root) doesn't reach them. (The parser/arena path is fine — this
is specific to hand-built trees.)
Impact
Any code that builds JSON with the creation API and frees the root leaks its
nested nodes. Notably contrib/tinyweb/schema_api's JSON:API error envelopes
(json.obj() nesting errors/source/etc.) leak on their error paths. It was
also why std.schema's to_json_schema() initially leaked — worked around there
by building the string with strbuilder instead of the JSON DOM (#1445), but the
underlying std.json issue remains.
Suggested fix
Make json_free reclaim builder-attached children on the heap path (or have
object_set/array_add link children into the root's arena/free-list so one
json_free(root) reclaims the whole tree), matching the parsed-tree behavior.
Summary
Building a JSON tree with the creation API (
json.obj()/json.arr()) andfreeing the root with
json.json_free(root)leaks the nested created objects/arrays.
json_freedoes not fully reclaim children attached viajson.set/json.pushon the builder (non-parsed) path.Minimal repro
The two nested
json_create_objectallocations (props,p) are lost. A singletop-level object frees fine; only nested-attached children leak.
Root cause (likely)
json_free(std/json/aether_json.c) frees via an arena when one is attached,else
heap_free_tree. Objects made with the creation API areheap_new'd withJV_FLAG_HEAP_STRUCT; children attached later viaobject_set/array_adddon'tappear to be linked into the parent's free path the way parsed-tree children are,
so
heap_free_tree(root)doesn't reach them. (The parser/arena path is fine — thisis specific to hand-built trees.)
Impact
Any code that builds JSON with the creation API and frees the root leaks its
nested nodes. Notably
contrib/tinyweb/schema_api's JSON:API error envelopes(
json.obj()nestingerrors/source/etc.) leak on their error paths. It wasalso why
std.schema'sto_json_schema()initially leaked — worked around thereby building the string with
strbuilderinstead of the JSON DOM (#1445), but theunderlying
std.jsonissue remains.Suggested fix
Make
json_freereclaim builder-attached children on the heap path (or haveobject_set/array_addlink children into the root's arena/free-list so onejson_free(root)reclaims the whole tree), matching the parsed-tree behavior.