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

WIP Dev/jae/crossrealm #2535

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
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
7 changes: 7 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,10 @@ func Make1() *p_crossrealm.Container {
B: local,
}
}

type Fooer interface{ Foo() }

var fooer Fooer

func SetFooer(f Fooer) { fooer = f }
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 @@

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 @@
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 @@
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) setCurrentPackage(pv *PackageValue) {
m.Package = pv
rlm := pv.GetRealm()
if rlm != nil {
m.Realm = rlm

Check warning on line 218 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L218

Added line #L218 was not covered by tests
}
}

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

Expand Down Expand Up @@ -1797,29 +1806,36 @@
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))

Check warning on line 1817 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L1817

Added line #L1817 was not covered by tests
}
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))

Check warning on line 1836 in gnovm/pkg/gnolang/machine.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/machine.go#L1836

Added line #L1836 was not covered by tests
}
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 @@
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 @@
if co.GetIsEscaped() {
// already escaped
} else {
rlm.MarkNewEscaped(co)
rlm.MarkNewEscapedCheckCrossRealm(co)

Check warning on line 173 in gnovm/pkg/gnolang/realm.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/realm.go#L173

Added line #L173 was not covered by tests
}
} else if co.GetIsReal() {
rlm.MarkDirty(co)
Expand Down Expand Up @@ -265,6 +264,24 @@
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

Check warning on line 272 in gnovm/pkg/gnolang/realm.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/realm.go#L272

Added line #L272 was not covered by tests
}
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)

Check warning on line 280 in gnovm/pkg/gnolang/realm.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/realm.go#L278-L280

Added lines #L278 - L280 were not covered by tests
}
rlm.MarkNewEscaped(oo)
}

func (rlm *Realm) MarkNewEscaped(oo Object) {
if debug {
if !oo.GetIsNewReal() && !oo.GetIsReal() {
Expand Down Expand Up @@ -300,8 +317,7 @@
len(rlm.newDeleted) > 0 ||
len(rlm.created) > 0 ||
len(rlm.updated) > 0 ||
len(rlm.deleted) > 0 ||
len(rlm.escaped) > 0 {
len(rlm.deleted) > 0 {

Check warning on line 320 in gnovm/pkg/gnolang/realm.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/realm.go#L320

Added line #L320 was not covered by tests
panic("realm updates in readonly transaction")
}
return
Expand All @@ -317,8 +333,7 @@
ensureUniq(rlm.updated)
if false ||
rlm.created != nil ||
rlm.deleted != nil ||
rlm.escaped != nil {
rlm.deleted != nil {

Check warning on line 336 in gnovm/pkg/gnolang/realm.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/realm.go#L336

Added line #L336 was not covered by tests
panic("realm should not have created, deleted, or escaped marks before beginning finalization")
}
}
Expand All @@ -337,7 +352,6 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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")

Check warning on line 587 in gnovm/pkg/gnolang/realm.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/realm.go#L587

Added line #L587 was not covered by tests
}

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

//----------------------------------------
Expand Down Expand Up @@ -789,7 +806,6 @@
rlm.created = nil
rlm.updated = nil
rlm.deleted = nil
rlm.escaped = nil
}

//----------------------------------------
Expand Down Expand Up @@ -1471,7 +1487,7 @@
} 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")

Check warning on line 1490 in gnovm/pkg/gnolang/realm.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/realm.go#L1490

Added line #L1490 was not covered by tests
}
}
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
30 changes: 30 additions & 0 deletions gnovm/tests/files/zrealm_crossrealm15.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// PKGPATH: gno.land/r/crossrealm_test
package crossrealm_test

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

type fooer struct{}

func (fooer) Foo() { println("hello") }

var f *fooer

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

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

// Output:
// hello
// .

// Error:

// Realm:
Loading
Loading