Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(gnovm): add test for bounding issue #2526

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bdb44bd
test(gnovm): add test for bounding issue
moul Jul 6, 2024
7cba9ca
chore: fixup
moul Jul 6, 2024
4ba7490
chore: fixup
moul Jul 6, 2024
621d599
chore: fixup
moul Jul 6, 2024
04bcd67
Merge branch 'master' into dev/moul/boundbug
moul Jul 6, 2024
b0a1e1f
allow passing in an object about to be persisted in the previous realm
jaekwon Jul 6, 2024
795f12f
Merge remote-tracking branch 'origin/master' into dev/moul/boundbug
moul Jul 7, 2024
4a2d67d
Merge remote-tracking branch 'origin/dev/jae/crossrealm' into dev/mou…
moul Jul 7, 2024
cbf2753
remove spurious lines
jaekwon Jul 7, 2024
5f647fb
fix for all cases
jaekwon Jul 7, 2024
bd889f7
reset new escape for both cases
jaekwon Jul 7, 2024
2a90a3d
Merge remote-tracking branch 'origin/dev/jae/crossrealm' into dev/mou…
moul Jul 7, 2024
00a2779
optimized fix
jaekwon Jul 7, 2024
ff894be
Merge remote-tracking branch 'origin/dev/jae/crossrealm' into dev/mou…
moul Jul 7, 2024
3a498f6
optimize
jaekwon Jul 7, 2024
60b99cc
add test for crossrealm new escaped internal objects
jaekwon Jul 7, 2024
5eb4447
Merge remote-tracking branch 'origin/dev/jae/crossrealm' into dev/mou…
moul Jul 7, 2024
03e838b
chore: fixup
moul Jul 7, 2024
71b74f4
chore: fixup
moul Jul 7, 2024
4ca57af
chore: fixup
moul Jul 7, 2024
19c2e79
chore: fixup
moul Jul 7, 2024
8014a85
chore: fixup
moul Jul 7, 2024
8e69d3a
Revert "chore: fixup"
moul Jul 7, 2024
a07ea6b
Merge remote-tracking branch 'origin/dev/jae/crossrealm' into dev/mou…
moul Jul 7, 2024
e206804
chore: fixup
moul Jul 7, 2024
a91c84a
change realm to receiver's
jaekwon Jul 7, 2024
ac86a26
Merge remote-tracking branch 'origin/dev/jae/crossrealm' into dev/mou…
moul Jul 7, 2024
91134ca
chore: fixup
moul Jul 7, 2024
c15ff58
chore: add new cross realm test
moul Aug 21, 2024
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
10 changes: 10 additions & 0 deletions examples/gno.land/r/demo/tests/crossrealm/crossrealm.gno
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ func Make1() *p_crossrealm.Container {
B: local,
}
}

type Fooer interface{ Foo() }

var fooer Fooer

func SetFooer(f Fooer) Fooer {
fooer = f
return fooer
}
func CallFoo() { fooer.Foo() }
2 changes: 2 additions & 0 deletions examples/gno.land/r/demo/tests/tests.gno
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type TestRealmObject struct {
Field string
}

var TestRealmObjectValue TestRealmObject

func ModifyTestRealmObject(t *TestRealmObject) {
t.Field += "_modified"
}
Expand Down
54 changes: 35 additions & 19 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ func (e Exception) Sprint(m *Machine) string {

type Machine struct {
// State
Ops []Op // main operations
NumOps int
Ops []Op // operations stack
NumOps int // number of operations
Values []TypedValue // buffer of values to be operated on
NumValues int // number of values
Exprs []Expr // pending expressions
Expand All @@ -47,9 +47,9 @@ type Machine struct {
Package *PackageValue // active package
Realm *Realm // active realm
Alloc *Allocator // memory allocations
Exceptions []Exception
NumResults int // number of results returned
Cycles int64 // number of "cpu" cycles
Exceptions []Exception // exceptions stack
NumResults int // number of results returned
Cycles int64 // number of "cpu" cycles performed

Debugger Debugger

Expand Down Expand Up @@ -199,6 +199,7 @@ func (m *Machine) Release() {
machinePool.Put(m)
}

// Convenience for initial setup of the machine.
func (m *Machine) SetActivePackage(pv *PackageValue) {
if err := m.CheckEmpty(); err != nil {
panic(errors.Wrap(err, "set package when machine not empty"))
Expand All @@ -210,6 +211,14 @@ func (m *Machine) SetActivePackage(pv *PackageValue) {
}
}

func (m *Machine) setCurrentPackage(pv *PackageValue) {
m.Package = pv
rlm := pv.GetRealm()
if rlm != nil {
m.Realm = rlm
}
}

//----------------------------------------
// top level Run* methods.

Expand Down Expand Up @@ -1797,29 +1806,36 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) {
m.Printf("+F %#v\n", fr)
}
m.Frames = append(m.Frames, fr)
pv := fv.GetPackage(m.Store)
if pv == nil {
panic(fmt.Sprintf("package value missing in store: %s", fv.PkgPath))
}
rlm := pv.GetRealm()
if rlm == nil && recv.IsDefined() {
if recv.IsDefined() {
// If the receiver is defined, we enter the receiver's realm.
obj := recv.GetFirstObject(m.Store)
if obj == nil {
// could be a nil receiver.
// just ignore.
// set package and realm of function.
pv := fv.GetPackage(m.Store)
if pv == nil {
panic(fmt.Sprintf("package value missing in store: %s", fv.PkgPath))
}
m.setCurrentPackage(pv) // maybe new realm
} else {
recvOID := obj.GetObjectInfo().ID
if !recvOID.IsZero() {
if recvOID.IsZero() {
// receiver isn't owned yet.
// just continue with current package and realm.
// XXX is this reasonable?
} else {
// override the pv and rlm with receiver's.
recvPkgOID := ObjectIDFromPkgID(recvOID.PkgID)
pv = m.Store.GetObject(recvPkgOID).(*PackageValue)
rlm = pv.GetRealm() // done
pv := m.Store.GetObject(recvPkgOID).(*PackageValue)
m.setCurrentPackage(pv) // maybe new realm
}
}
}
m.Package = pv
if rlm != nil && m.Realm != rlm {
m.Realm = rlm // enter new realm
} else {
pv := fv.GetPackage(m.Store)
if pv == nil {
panic(fmt.Sprintf("package value missing in store: %s", fv.PkgPath))
}
m.setCurrentPackage(pv) // maybe new realm
}
}

Expand Down
37 changes: 21 additions & 16 deletions gnovm/pkg/gnolang/ownership.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,12 @@ type ObjectInfo struct {
RefCount int // for persistence. deleted/gc'd if 0.
IsEscaped bool `json:",omitempty"` // hash in iavl.
// MemRefCount int // consider for optimizations.
isDirty bool
isDeleted bool
isNewReal bool
isNewEscaped bool
isNewDeleted bool
isDirty bool
isDeleted bool
isNewReal bool
isNewEscaped bool
isNewDeleted bool
lastNewRealEscapedRealm PkgID

// XXX huh?
owner Object // mem reference to owner.
Expand All @@ -154,17 +155,21 @@ type ObjectInfo struct {
// Note that "owner" is nil.
func (oi *ObjectInfo) Copy() ObjectInfo {
return ObjectInfo{
ID: oi.ID,
Hash: oi.Hash.Copy(),
OwnerID: oi.OwnerID,
ModTime: oi.ModTime,
RefCount: oi.RefCount,
IsEscaped: oi.IsEscaped,
isDirty: oi.isDirty,
isDeleted: oi.isDeleted,
isNewReal: oi.isNewReal,
isNewEscaped: oi.isNewEscaped,
isNewDeleted: oi.isNewDeleted,
ID: oi.ID,
Hash: oi.Hash.Copy(),
OwnerID: oi.OwnerID,
ModTime: oi.ModTime,
RefCount: oi.RefCount,
IsEscaped: oi.IsEscaped,
/*
// XXX do the following need copying too?
isDirty: oi.isDirty,
isDeleted: oi.isDeleted,
isNewReal: oi.isNewReal,
isNewEscaped: oi.isNewEscaped,
isNewDeleted: oi.isNewDeleted,
lastNewRealEscapedRealm: oi.lastNewRealEscapedRealm,
*/
}
}

Expand Down
48 changes: 32 additions & 16 deletions gnovm/pkg/gnolang/realm.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ type Realm struct {
created []Object // about to become real.
updated []Object // real objects that were modified.
deleted []Object // real objects that became deleted.
escaped []Object // real objects with refcount > 1.
}

// Creates a blank new realm with counter 0.
Expand Down Expand Up @@ -171,7 +170,7 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) {
if co.GetIsEscaped() {
// already escaped
} else {
rlm.MarkNewEscaped(co)
rlm.MarkNewEscapedCheckCrossRealm(co)
}
} else if co.GetIsReal() {
rlm.MarkDirty(co)
Expand Down Expand Up @@ -265,6 +264,24 @@ func (rlm *Realm) MarkNewDeleted(oo Object) {
rlm.newDeleted = append(rlm.newDeleted, oo)
}

func (rlm *Realm) MarkNewEscapedCheckCrossRealm(oo Object) {
oi := oo.GetObjectInfo()
if oi.lastNewRealEscapedRealm == rlm.ID {
// already processed for this realm,
// see below.
return
}
if !oi.GetIsReal() {
// this can happen if a ref +1
// new object gets passed into
// an external realm function.
oi.lastNewRealEscapedRealm = rlm.ID
oi.SetIsNewReal(false)
rlm.MarkNewReal(oo)
}
rlm.MarkNewEscaped(oo)
}

func (rlm *Realm) MarkNewEscaped(oo Object) {
if debug {
if !oo.GetIsNewReal() && !oo.GetIsReal() {
Expand Down Expand Up @@ -300,8 +317,7 @@ func (rlm *Realm) FinalizeRealmTransaction(readonly bool, store Store) {
len(rlm.newDeleted) > 0 ||
len(rlm.created) > 0 ||
len(rlm.updated) > 0 ||
len(rlm.deleted) > 0 ||
len(rlm.escaped) > 0 {
len(rlm.deleted) > 0 {
panic("realm updates in readonly transaction")
}
return
Expand All @@ -317,8 +333,7 @@ func (rlm *Realm) FinalizeRealmTransaction(readonly bool, store Store) {
ensureUniq(rlm.updated)
if false ||
rlm.created != nil ||
rlm.deleted != nil ||
rlm.escaped != nil {
rlm.deleted != nil {
panic("realm should not have created, deleted, or escaped marks before beginning finalization")
}
}
Expand All @@ -337,7 +352,6 @@ func (rlm *Realm) FinalizeRealmTransaction(readonly bool, store Store) {
rlm.markDirtyAncestors(store)
if debug {
ensureUniq(rlm.created, rlm.updated, rlm.deleted)
ensureUniq(rlm.escaped)
}
// save all the created and updated objects.
// hash calculation is done along the way,
Expand All @@ -359,7 +373,12 @@ func (rlm *Realm) FinalizeRealmTransaction(readonly bool, store Store) {
// and get assigned ids.
func (rlm *Realm) processNewCreatedMarks(store Store) {
// Create new objects and their new descendants.
for _, oo := range rlm.newCreated {
// NOTE: the following range does not work
// because incRefCreatedDescendants may append to newCreated
// for the case when new escapes are found to have crossed.
// BAD: for _, oo := range rlm.newCreated {
for i := 0; i < len(rlm.newCreated); i++ {
oo := rlm.newCreated[i]
if debug {
if oo.GetIsDirty() {
panic("new created mark cannot be dirty")
Expand Down Expand Up @@ -442,8 +461,7 @@ func (rlm *Realm) incRefCreatedDescendants(store Store, oo Object) {
// NOTE: do not unset owner here,
// may become unescaped later
// in processNewEscapedMarks().
// NOTE: may already be escaped.
rlm.MarkNewEscaped(child)
rlm.MarkNewEscapedCheckCrossRealm(child)
}
} else {
panic("child reference count should be greater than zero after increasing")
Expand Down Expand Up @@ -523,7 +541,6 @@ func (rlm *Realm) decRefDeletedDescendants(store Store, oo Object) {
// objects get their original owners marked dirty (to be further
// marked via markDirtyAncestors).
func (rlm *Realm) processNewEscapedMarks(store Store) {
escaped := make([]Object, 0, len(rlm.newEscaped))
// These are those marked by MarkNewEscaped(),
// regardless of whether new-real or was real,
// but is always newly escaped,
Expand All @@ -549,7 +566,6 @@ func (rlm *Realm) processNewEscapedMarks(store Store) {
// we do that upon actually persisting
// the hash index.
// eo.SetIsNewEscaped(false)
escaped = append(escaped, eo)

// add to escaped, and mark dirty previous owner.
po := getOwner(store, eo)
Expand All @@ -566,15 +582,16 @@ func (rlm *Realm) processNewEscapedMarks(store Store) {
// exists, mark dirty.
rlm.MarkDirty(po)
}
// TODO: move to if debug { } once proven.
if eo.GetObjectID().IsZero() {
panic("new escaped mark has no object ID")
panic("new escaped object has no object ID")
}

// escaped has no owner.
eo.SetOwner(nil)
}
}
}
rlm.escaped = escaped // XXX is this actually used?
}

//----------------------------------------
Expand Down Expand Up @@ -789,7 +806,6 @@ func (rlm *Realm) clearMarks() {
rlm.created = nil
rlm.updated = nil
rlm.deleted = nil
rlm.escaped = nil
}

//----------------------------------------
Expand Down Expand Up @@ -1471,7 +1487,7 @@ func toRefValue(val Value) RefValue {
} else if oo.GetIsEscaped() {
if debug {
if !oo.GetOwnerID().IsZero() {
panic("cannot convert escaped object to ref value without an owner ID")
panic("cannot convert escaped object to ref value with an owner ID")
}
}
return RefValue{
Expand Down
1 change: 1 addition & 0 deletions gnovm/pkg/gnomod/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

const testRemote string = "test3.gno.land:26657"

// XXX this test fails when the network is offline.
func TestFetchDeps(t *testing.T) {
for _, tc := range []struct {
desc string
Expand Down
1 change: 0 additions & 1 deletion gnovm/tests/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error {
panic(fmt.Sprintf("fail on %s: got unexpected debug error(s): %v", path, gno.GetDebugErrors()))
}
// pnc is nil, errWanted empty, no gno debug errors
return nil
}
case "Output":
// panic if got unexpected error
Expand Down
32 changes: 32 additions & 0 deletions gnovm/tests/files/zrealm_crossrealm15_stdlibs.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// PKGPATH: gno.land/r/crossrealm_test
package crossrealm_test

import (
"std"

crossrealm "gno.land/r/demo/tests/crossrealm"
)

type fooer struct{}

func (fooer) Foo() { println("hello " + std.CurrentRealm().PkgPath()) }

var f *fooer

func init() {
f = &fooer{}
crossrealm.SetFooer(f)
crossrealm.CallFoo()
}

func main() {
print(".")
}

// Output:
// hello gno.land/r/demo/tests/crossrealm
// .

// Error:

// Realm:
Loading
Loading