From 9c5fcde65e0717b5f3bd6f0946ff790cb8b73514 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Wed, 18 Oct 2023 19:35:45 -0700 Subject: [PATCH 1/5] initial commit of receiver implied realm for pkg methods --- gnovm/pkg/gnolang/machine.go | 13 +++++++++++++ gnovm/pkg/gnolang/realm.go | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 94232e014d2..f6b0d2ed4c1 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1629,6 +1629,19 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) { rlm := pv.GetRealm() if rlm != nil && m.Realm != rlm { m.Realm = rlm // enter new realm + } else if rlm == nil && recv.V != nil { // XXX maybe improve this part. + // maybe this is a bound method of a recv of a realm. + // in that case, inherit the realm of the receiver. + obj, ok := recv.V.(Object) + if ok { + recvOID := obj.GetObjectInfo().ID + if !recvOID.IsZero() { + recvPVOID := ObjectIDFromPkgID(recvOID.PkgID) + pv := m.Store.GetObject(recvPVOID).(*PackageValue) + rlm := pv.GetRealm() + m.Realm = rlm + } + } } } diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 567aea58284..66f3ff56edf 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -70,12 +70,21 @@ func (pid PkgID) Bytes() []byte { } func PkgIDFromPkgPath(path string) PkgID { + fmt.Printf("PkgPath %s -> PkgID %v", path, + PkgID{HashBytes([]byte(path))}) return PkgID{HashBytes([]byte(path))} } +// Returns the ObjectID of the PackageValue associated with path. func ObjectIDFromPkgPath(path string) ObjectID { + pkgID := PkgIDFromPkgPath(path) + return ObjectIDFromPkgID(pkgID) +} + +// Returns the ObjectID of the PackageValue associated with pkgID. +func ObjectIDFromPkgID(pkgID PkgID) ObjectID { return ObjectID{ - PkgID: PkgIDFromPkgPath(path), + PkgID: pkgID, NewTime: 1, // by realm logic. } } @@ -145,6 +154,10 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) { return // do nothing. } if po.GetObjectID().PkgID != rlm.ID { + fmt.Println("PO", po.String()) + fmt.Println("PO.PKGID", po.GetObjectID().PkgID) + fmt.Println("rlm", rlm) + fmt.Println("rlm.ID", rlm.ID) panic("cannot modify external-realm or non-realm object") } // From here on, po is real (not new-real). From a9bee164dcd3ceec2e3c856a21c110a278712cd5 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Wed, 18 Oct 2023 20:16:54 -0700 Subject: [PATCH 2/5] deref receiver before checking if object --- gnovm/pkg/gnolang/machine.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index f6b0d2ed4c1..95f1a90fa2a 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1632,7 +1632,18 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) { } else if rlm == nil && recv.V != nil { // XXX maybe improve this part. // maybe this is a bound method of a recv of a realm. // in that case, inherit the realm of the receiver. - obj, ok := recv.V.(Object) + recvv := recv.V + // deref if pointer. + // XXX I guess we want to deref as much as possible. + for { + if pv, ok := recvv.(PointerValue); ok { + recvv = pv.Deref().V + } else { + break + } + } + // Now check if it is an object. + obj, ok := recvv.(Object) if ok { recvOID := obj.GetObjectInfo().ID if !recvOID.IsZero() { From bc47c25f45ebf691d3db4ec6015dba4bd9155d9c Mon Sep 17 00:00:00 2001 From: jaekwon Date: Wed, 18 Oct 2023 20:23:56 -0700 Subject: [PATCH 3/5] ... --- gnovm/pkg/gnolang/realm.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 66f3ff56edf..f4a4b17dcb5 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -70,8 +70,8 @@ func (pid PkgID) Bytes() []byte { } func PkgIDFromPkgPath(path string) PkgID { - fmt.Printf("PkgPath %s -> PkgID %v", path, - PkgID{HashBytes([]byte(path))}) + // fmt.Printf("PkgPath %s -> PkgID %v", path, + // PkgID{HashBytes([]byte(path))}) return PkgID{HashBytes([]byte(path))} } @@ -154,10 +154,10 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) { return // do nothing. } if po.GetObjectID().PkgID != rlm.ID { - fmt.Println("PO", po.String()) - fmt.Println("PO.PKGID", po.GetObjectID().PkgID) - fmt.Println("rlm", rlm) - fmt.Println("rlm.ID", rlm.ID) + // fmt.Println("PO", po.String()) + // fmt.Println("PO.PKGID", po.GetObjectID().PkgID) + // fmt.Println("rlm", rlm) + // fmt.Println("rlm.ID", rlm.ID) panic("cannot modify external-realm or non-realm object") } // From here on, po is real (not new-real). From ec4d71734048712f1bf3ab7ceb6e8750c82e5314 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Thu, 19 Oct 2023 06:17:43 -0700 Subject: [PATCH 4/5] ... --- gnovm/pkg/gnolang/machine.go | 36 +++++++++++++--------------------- gnovm/pkg/gnolang/ownership.go | 1 + 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 95f1a90fa2a..8665e049e9f 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1627,33 +1627,25 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) { } m.Package = pv rlm := pv.GetRealm() - if rlm != nil && m.Realm != rlm { - m.Realm = rlm // enter new realm - } else if rlm == nil && recv.V != nil { // XXX maybe improve this part. - // maybe this is a bound method of a recv of a realm. - // in that case, inherit the realm of the receiver. - recvv := recv.V - // deref if pointer. - // XXX I guess we want to deref as much as possible. - for { - if pv, ok := recvv.(PointerValue); ok { - recvv = pv.Deref().V - } else { - break - } - } - // Now check if it is an object. - obj, ok := recvv.(Object) - if ok { + if rlm == nil && recv.IsDefined() { + // if bound method, get realm from receiver. + obj := recv.GetFirstObject(m.Store) + if obj == nil { + // panic("XXX not sure why this would be") + fmt.Println("XXX XXX", recv.String()) + } else { recvOID := obj.GetObjectInfo().ID if !recvOID.IsZero() { - recvPVOID := ObjectIDFromPkgID(recvOID.PkgID) - pv := m.Store.GetObject(recvPVOID).(*PackageValue) - rlm := pv.GetRealm() - m.Realm = rlm + recvPkgOID := ObjectIDFromPkgID(recvOID.PkgID) + pv := m.Store.GetObject(recvPkgOID).(*PackageValue) + rlm = pv.GetRealm() // done } } } + if rlm != nil && m.Realm != rlm { + // enter new realm + m.Realm = rlm + } } func (m *Machine) PushFrameGoNative(cx *CallExpr, fv *NativeValue) { diff --git a/gnovm/pkg/gnolang/ownership.go b/gnovm/pkg/gnolang/ownership.go index f2afc393d05..39f9af27ecb 100644 --- a/gnovm/pkg/gnolang/ownership.go +++ b/gnovm/pkg/gnolang/ownership.go @@ -332,6 +332,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). + // XXX is there an issue with Base=nil pointers here cross realm? if cv.Base != nil { return cv.Base.(Object) } else { From 75b1d104ddd65d8135668a505c0ea9f78db0b5d4 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Thu, 19 Oct 2023 14:10:14 -0700 Subject: [PATCH 5/5] add realmtest --- .../demo/tests/p_crossrealm/p_crossrealm.gno | 24 +++++++++++++++ .../r/demo/tests/crossrealm/crossrealm.gno | 29 +++++++++++++++++++ gnovm/pkg/gnolang/machine.go | 6 +++- gnovm/tests/files/zrealm_crossrealm13.gno | 17 +++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 examples/gno.land/p/demo/tests/p_crossrealm/p_crossrealm.gno create mode 100644 examples/gno.land/r/demo/tests/crossrealm/crossrealm.gno create mode 100644 gnovm/tests/files/zrealm_crossrealm13.gno diff --git a/examples/gno.land/p/demo/tests/p_crossrealm/p_crossrealm.gno b/examples/gno.land/p/demo/tests/p_crossrealm/p_crossrealm.gno new file mode 100644 index 00000000000..6d46203e98c --- /dev/null +++ b/examples/gno.land/p/demo/tests/p_crossrealm/p_crossrealm.gno @@ -0,0 +1,24 @@ +package p_crossrealm + +type Stringer interface { + String() string +} + +type Container struct { + A int + B Stringer +} + +func (c *Container) Touch() *Container { + c.A += 1 + return c +} + +func (c *Container) Print() { + println("A:", c.A) + if c.B == nil { + println("B: undefined") + } else { + println("B:", c.B.String()) + } +} diff --git a/examples/gno.land/r/demo/tests/crossrealm/crossrealm.gno b/examples/gno.land/r/demo/tests/crossrealm/crossrealm.gno new file mode 100644 index 00000000000..97273f642de --- /dev/null +++ b/examples/gno.land/r/demo/tests/crossrealm/crossrealm.gno @@ -0,0 +1,29 @@ +package crossrealm + +import ( + "gno.land/p/demo/tests/p_crossrealm" + "gno.land/p/demo/ufmt" +) + +type LocalStruct struct { + A int +} + +func (ls *LocalStruct) String() string { + return ufmt.Sprintf("LocalStruct{%d}", ls.A) +} + +// local is saved locally in this realm +var local *LocalStruct + +func init() { + local = &LocalStruct{A: 123} +} + +// Make1 returns a local object wrapped by a p struct +func Make1() *p_crossrealm.Container { + return &p_crossrealm.Container{ + A: 1, + B: local, + } +} diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 8665e049e9f..b01c7e5ca80 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -221,7 +221,11 @@ func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode } else { pn = NewPackageNode(Name(memPkg.Name), memPkg.Path, &FileSet{}) pv = pn.NewPackage() - m.Store.SetBlockNode(pn) + if true { // TODO finish SetBlockNode() + m.Store.SetBlockNode(pn) + } else { + // TODO m.Store.SetCacheBlockNode(pn) + } m.Store.SetCachePackage(pv) } m.SetActivePackage(pv) diff --git a/gnovm/tests/files/zrealm_crossrealm13.gno b/gnovm/tests/files/zrealm_crossrealm13.gno new file mode 100644 index 00000000000..23451e6f5d1 --- /dev/null +++ b/gnovm/tests/files/zrealm_crossrealm13.gno @@ -0,0 +1,17 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +import ( + crossrealm "gno.land/r/demo/tests/crossrealm" +) + +func main() { + // even though we are running within a realm, + // we aren't storing the result of crossrealm.Make1(), + // so this should print fine. + crossrealm.Make1().Touch().Print() +} + +// Output: +// A: 2 +// B: LocalStruct{123}