Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions gnovm/pkg/gnolang/uverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -1013,12 +1013,15 @@ func makeUverseNode() {
if !ok {
return
}
// delete
mv.DeleteForKey(m, m.Store, &itv)

// mark key as deleted
keyObj := itv.GetFirstObject(m.Store)
m.Realm.DidUpdate(m, mv, keyObj, nil)
// delete; capture the STORED key that was removed. Detaching
// the argument key (itv) instead orphaned the stored key object
// in the store — it was never DecRef'd nor marked deleted.
delKey := mv.DeleteForKey(m, m.Store, &itv)

// mark the STORED key object as deleted (not the argument key)
if delKey != nil {
m.Realm.DidUpdate(m, mv, delKey.GetFirstObject(m.Store), nil)
}

// mark value as deleted
valObj := val.GetFirstObject(m.Store)
Expand Down
10 changes: 8 additions & 2 deletions gnovm/pkg/gnolang/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -821,16 +821,22 @@ func (mv *MapValue) GetValueForKey(m *Machine, store Store, key *TypedValue) (va
return
}

func (mv *MapValue) DeleteForKey(m *Machine, store Store, key *TypedValue) {
// DeleteForKey removes the entry for key and returns the STORED key TypedValue
// that was removed (nil if the key was absent or NaN). Callers must dirty-mark /
// DecRef this stored key's object, not the (possibly transient) argument key —
// otherwise a non-primitive stored key object is orphaned in the store.
func (mv *MapValue) DeleteForKey(m *Machine, store Store, key *TypedValue) (deletedKey *TypedValue) {
// if key is NaN, do nothing.
kmk, isNaN := key.ComputeMapKey(m, store, false)
if isNaN {
return
return nil
}
if mli, ok := mv.vmap[kmk]; ok {
mv.List.Remove(mli)
delete(mv.vmap, kmk)
return &mli.Key
}
return nil
}
Comment on lines +828 to 840

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeleteForKey has the machine and could mark the removed key itself instead of returning it for the builtin. Is that deliberate, to keep DidUpdate out of MapValue?


// ----------------------------------------
Expand Down
96 changes: 96 additions & 0 deletions gnovm/tests/files/zrealm_map5.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// PKGPATH: gno.land/r/test
package test

// Regression: deleting an entry whose KEY is a non-primitive (array/struct)
// object from a persisted realm map must reclaim the stored key object — it
// must be emitted as a deleted object (d[...]) in the finalize diff, not
// orphaned. Before the fix the stored [1]int key object leaked (delete
// detached the argument key, not the stored key).
var m map[[1]int]int

func init() {
m = map[[1]int]int{[1]int{1}: 10, [1]int{2}: 20}
}

func main(cur realm) {
delete(m, [1]int{1})
println("len:", len(m))
}

// Output:
// len: 1

// Realm:
// finalizerealm["gno.land/r/test"]
// u[08ada09dee16d791fd406d629fe29bb0ed084a30:7](-202)=
// @@ -1,31 +1,6 @@
// {
// "List": {
// "List": [
// - {
// - "Key": {
// - "T": {
// - "@type": "/gno.ArrayType",
// - "Elt": {
// - "@type": "/gno.PrimitiveType",
// - "value": "32"
// - },
// - "Len": "1",
// - "Vrd": false
// - },
// - "V": {
// - "@type": "/gno.RefValue",
// - "Hash": "983a2dfd65f409988f2165b8b8e31a3f0147a0d7",
// - "ObjectID": "08ada09dee16d791fd406d629fe29bb0ed084a30:8"
// - }
// - },
// - "Value": {
// - "N": "CgAAAAAAAAA=",
// - "T": {
// - "@type": "/gno.PrimitiveType",
// - "value": "32"
// - }
// - }
// - },
// {
// "Key": {
// "T": {
// @@ -56,7 +31,7 @@
// "ObjectInfo": {
// "ID": "08ada09dee16d791fd406d629fe29bb0ed084a30:7",
// "LastObjectSize": "590",
// - "ModTime": "0",
// + "ModTime": "9",
// "OwnerID": "08ada09dee16d791fd406d629fe29bb0ed084a30:3",
// "RefCount": "1"
// }
// u[08ada09dee16d791fd406d629fe29bb0ed084a30:3](0)=
// @@ -2,7 +2,7 @@
// "ObjectInfo": {
// "ID": "08ada09dee16d791fd406d629fe29bb0ed084a30:3",
// "LastObjectSize": "393",
// - "ModTime": "6",
// + "ModTime": "9",
// "OwnerID": "08ada09dee16d791fd406d629fe29bb0ed084a30:2",
// "RefCount": "1"
// },
// @@ -25,7 +25,7 @@
// },
// "V": {
// "@type": "/gno.RefValue",
// - "Hash": "a669cab1a0b6e73131ef8ee77e2ed6bedcf482e3",
// + "Hash": "b8ca988c204aac98121d376916a114a3f0bee2cd",
// "ObjectID": "08ada09dee16d791fd406d629fe29bb0ed084a30:7"
// }
// }
// u[08ada09dee16d791fd406d629fe29bb0ed084a30:2](0)=
// @@ -34,7 +34,7 @@
// },
// "V": {
// "@type": "/gno.RefValue",
// - "Hash": "e000ab9359964c6da1719766969d91e0ab8de8dd",
// + "Hash": "b3f9131b15661e4bfa46d47af69a74439c92d91c",
// "ObjectID": "08ada09dee16d791fd406d629fe29bb0ed084a30:3"
// }
// },
// d[08ada09dee16d791fd406d629fe29bb0ed084a30:8](-213)