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

feat: Bound Method Realm support #1257

Merged
merged 8 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
24 changes: 24 additions & 0 deletions examples/gno.land/p/demo/tests/p_crossrealm/p_crossrealm.gno
Original file line number Diff line number Diff line change
@@ -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())
}
}
29 changes: 29 additions & 0 deletions examples/gno.land/r/demo/tests/crossrealm/crossrealm.gno
Original file line number Diff line number Diff line change
@@ -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,
}
}
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestRunApp(t *testing.T) {
},
{
args: []string{"run", "-debug-addr", "invalidhost:17538", "../../tests/integ/debugger/sample.gno"},
errShouldContain: "listen tcp: lookup invalidhost",
errShouldContain: "listen tcp",
},
{
args: []string{"run", "../../tests/integ/invalid_assign/main.gno"},
Expand Down
71 changes: 71 additions & 0 deletions gnovm/cmd/gno/testdata/gno_test/realm_boundmethod.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Set up GNOROOT in the current directory.
mkdir $WORK/gnovm
symlink $WORK/gnovm/stdlibs -> $GNOROOT/gnovm/stdlibs
env GNOROOT=$WORK

gno test -v ./examples/gno.land/r/demo/realm2

stderr '=== RUN TestDo'
stderr '--- PASS: TestDo.*'

stderr '=== RUN file/realm2_filetest.gno'
stderr '--- PASS: file/realm2_filetest.*'

-- examples/gno.land/p/demo/counter/gno.mod --
module gno.land/p/demo/counter

-- examples/gno.land/p/demo/counter/counter.gno --
package counter

type Counter struct {
n int
}

func (c *Counter) Inc() {
c.n++
}

-- examples/gno.land/r/demo/realm1/realm1.gno --
package realm1

import "gno.land/p/demo/counter"

var c = counter.Counter{}

func GetCounter() *counter.Counter {
return &c
}

-- examples/gno.land/r/demo/realm2/realm2.gno --
package realm2

import (
"gno.land/r/demo/realm1"
)

func Do() {
realm1.GetCounter().Inc()
}

-- examples/gno.land/r/demo/realm2/realm2_filetest.gno --
// PKGPATH: gno.land/r/tests
package tests

import "gno.land/r/demo/realm2"

func main() {
realm2.Do()
println("OK")
}

// Output:
// OK

-- examples/gno.land/r/demo/realm2/realm2_test.gno --
package realm2

import "testing"

func TestDo(t *testing.T) {
Do()
}
17 changes: 16 additions & 1 deletion gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1738,8 +1738,23 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) {
if pv == nil {
panic(fmt.Sprintf("package value missing in store: %s", fv.PkgPath))
}
m.Package = pv
rlm := pv.GetRealm()
if rlm == nil && recv.IsDefined() {
obj := recv.GetFirstObject(m.Store)
if obj == nil {
// could be a nil receiver.
// just ignore.
} else {
recvOID := obj.GetObjectInfo().ID
if !recvOID.IsZero() {
// override the pv and rlm with receiver's.
recvPkgOID := ObjectIDFromPkgID(recvOID.PkgID)
pv = m.Store.GetObject(recvPkgOID).(*PackageValue)
rlm = pv.GetRealm() // done
}
}
}
m.Package = pv
if rlm != nil && m.Realm != rlm {
m.Realm = rlm // enter new realm
}
Expand Down
5 changes: 0 additions & 5 deletions gnovm/pkg/gnolang/ownership.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,6 @@ func (oi *ObjectInfo) GetIsTransient() bool {
func (tv *TypedValue) GetFirstObject(store Store) Object {
switch cv := tv.V.(type) {
case PointerValue:
// TODO: in the future, consider skipping the base if persisted
// ref-count would be 1, e.g. only this pointer refers to
// 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.GetBase(store)
case *ArrayValue:
return cv
Expand Down
9 changes: 8 additions & 1 deletion gnovm/pkg/gnolang/realm.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,16 @@ func PkgIDFromPkgPath(path string) PkgID {
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.
}
}
Expand Down
17 changes: 17 additions & 0 deletions gnovm/tests/files/zrealm_crossrealm14.gno
Original file line number Diff line number Diff line change
@@ -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}
Loading