From 5393698ee6c5a455b4dad23c3b8c6c6ca0cb5f4a Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 2 Jul 2024 18:30:55 +0100 Subject: [PATCH] fix(gnovm): Resolve RefValues before using as Objects (#2454) Closes #2449. I believe the bug was introduced by #2255. Previously, if the type of `tv.V` in `GetFirstObject` was a `PointerValue`, it the `Base` would have been nil in the scenario producing this bug. Now that `Base` can never be nil for a `PointerValue`, the nil base check has been removed. We do however need to account for a new scenario where the `Base` is a `RefValue`, a type that does implement the `Object` interface. In the case where the base is a `RefValue`, first resolve it by calling `store.GetObject`.
Contributors' checklist... - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [x] Provided any useful hints for running manual tests - [x] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- gnovm/pkg/gnolang/ownership.go | 2 +- gnovm/pkg/gnolang/values.go | 4 +- gnovm/tests/files/issue-2449.gno | 65 ++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 gnovm/tests/files/issue-2449.gno diff --git a/gnovm/pkg/gnolang/ownership.go b/gnovm/pkg/gnolang/ownership.go index 24d70b5dd84..16aeb905173 100644 --- a/gnovm/pkg/gnolang/ownership.go +++ b/gnovm/pkg/gnolang/ownership.go @@ -333,7 +333,7 @@ func (tv *TypedValue) GetFirstObject(store Store) Object { // something in it; in that case, ignore the base. That will // likely require maybe a preparation step in persistence // ( or unlikely, a second type of ref-counting). - return cv.Base.(Object) + return cv.GetBase(store) case *ArrayValue: return cv case *SliceValue: diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index d38c083428c..ef367f2c480 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -192,7 +192,6 @@ const ( PointerIndexNative = -3 // Base is *NativeValue. ) -/* func (pv *PointerValue) GetBase(store Store) Object { switch cbase := pv.Base.(type) { case nil: @@ -204,10 +203,9 @@ func (pv *PointerValue) GetBase(store Store) Object { case Object: return cbase default: - panic("should not happen") + panic(fmt.Sprintf("unexpected pointer base type %T", cbase)) } } -*/ // cu: convert untyped; pass false for const definitions // TODO: document as something that enables into-native assignment. diff --git a/gnovm/tests/files/issue-2449.gno b/gnovm/tests/files/issue-2449.gno new file mode 100644 index 00000000000..6af4dbafc6c --- /dev/null +++ b/gnovm/tests/files/issue-2449.gno @@ -0,0 +1,65 @@ +// PKGPATH: gno.land/r/evt_test +package evt_test + +type Event struct { + name string +} + +var deletionEvents = []*Event{ + {name: "event1"}, + {name: "event2"}, + {name: "event3"}, + {name: "event4"}, +} + +var insertEvents = []*Event{ + {name: "event1"}, + {name: "event2"}, +} + +var appendEvents = []*Event{ + {name: "event1"}, +} + +func DelEvent(name string) { + for i, event := range deletionEvents { + if event.name == name { + deletionEvents = append(deletionEvents[:i], deletionEvents[i+1:]...) + return + } + } +} + +func InsertEvent(name string) { + insertEvents = append(insertEvents[:1], append([]*Event{{name: name}}, insertEvents[1:]...)...) +} + +func AppendEvent(name string) { + appendEvents = append(appendEvents, &Event{name: name}) +} + +func printEvents(events []*Event) { + for _, event := range events { + println(event.name) + } +} + +func main() { + DelEvent("event2") + InsertEvent("event1.5") + AppendEvent("event2") + + printEvents(deletionEvents) + printEvents(insertEvents) + printEvents(appendEvents) +} + +// Output: +// event1 +// event3 +// event4 +// event1 +// event1.5 +// event2 +// event1 +// event2