From 4053dcf228085397ac551e29897a98497620fe07 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 23 Feb 2024 15:28:18 -0500 Subject: [PATCH 01/23] first pass fixing type assertion bugs --- gnovm/pkg/gnolang/op_expressions.go | 64 ++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index b3bf240aea1..8ced68a2369 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -209,8 +209,32 @@ func (m *Machine) doOpTypeAssert1() { xv := m.PeekValue(1) xt := xv.T + // xt may be nil, but we need to wait to return because the value of xt that is set + // will depend on whether we are trying to assert to an interface or concrete type. + // xt can be nil in the case where recover can't find a panic to recover from and + // returns a bare TypedValue{}. + if t.Kind() == InterfaceKind { // is interface assert + if xt == nil { + // TODO: default panic type? + ex := fmt.Sprintf("nil doesn't implement %s", t.String()) + m.Panic(typedString(ex)) + return + } + if it, ok := baseOf(t).(*InterfaceType); ok { + // An interface type assertion on a value that doesn't have a concrete base + // type should always fail. + if _, ok := baseOf(xt).(*InterfaceType); ok { + // TODO: default panic type? + ex := fmt.Sprintf( + "non-concrete %s doesn't implement %s", + xt.String(), + it.String()) + m.Panic(typedString(ex)) + return + } + // t is Gno interface. // assert that x implements type. impl := false @@ -273,14 +297,34 @@ func (m *Machine) doOpTypeAssert1() { func (m *Machine) doOpTypeAssert2() { m.PopExpr() // peek type for re-use - tv := m.PeekValue(1) - t := tv.GetType() + tv := m.PeekValue(1) // boolean result + t := tv.GetType() // type being asserted + // peek x for re-use - xv := m.PeekValue(2) - xt := xv.T + xv := m.PeekValue(2) // value result + xt := xv.T // underlying value's type + + // xt may be nil, but we need to wait to return because the value of xt that is set + // will depend on whether we are trying to assert to an interface or concrete type. + // xt can be nil in the case where recover can't find a panic to recover from and + // returns a bare TypedValue{}. if t.Kind() == InterfaceKind { // is interface assert + if xt == nil { + *xv = TypedValue{} + *tv = untypedBool(false) + return + } + if it, ok := baseOf(t).(*InterfaceType); ok { + // An interface type assertion on a value that doesn't have a concrete base + // type should always fail. + if _, ok := baseOf(xt).(*InterfaceType); ok { + *xv = TypedValue{} + *tv = untypedBool(false) + return + } + // t is Gno interface. // assert that x implements type. impl := false @@ -314,11 +358,21 @@ func (m *Machine) doOpTypeAssert2() { panic("should not happen") } } else { // is concrete assert + if xt == nil { + *xv = TypedValue{ + T: t, + V: defaultValue(m.Alloc, t), + } + *tv = untypedBool(false) + return + } + tid := t.TypeID() xtid := xt.TypeID() // assert that x is of type. same := tid == xtid - if same { + + if same && xt != nil { // *xv = *xv *tv = untypedBool(true) } else { From 36ef721ad84f0158770e54f400790faded19f483 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 23 Feb 2024 16:43:07 -0500 Subject: [PATCH 02/23] applied similar bug fix to native interface type assertions --- gnovm/pkg/gnolang/op_expressions.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 8ced68a2369..1f3110b9fdc 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -254,16 +254,23 @@ func (m *Machine) doOpTypeAssert1() { } else if nt, ok := baseOf(t).(*NativeType); ok { // t is Go interface. // assert that x implements type. - impl := false + var nonConcrete string + var impl bool if nxt, ok := xt.(*NativeType); ok { - impl = nxt.Type.Implements(nt.Type) - } else { - impl = false + // If the underlying native type is reflect.Interface kind, then this has no + // concrete value and should fail. + if nxt.Type.Kind() != reflect.Interface { + impl = nxt.Type.Implements(nt.Type) + } else { + nonConcrete = "non-concrete " + } } + if !impl { // TODO: default panic type? ex := fmt.Sprintf( - "%s doesn't implement %s", + "%s%s doesn't implement %s", + nonConcrete, xt.String(), nt.String()) m.Panic(typedString(ex)) @@ -339,14 +346,18 @@ func (m *Machine) doOpTypeAssert2() { *tv = untypedBool(false) } } else if nt, ok := baseOf(t).(*NativeType); ok { + // If the value being asserted on is nil, it can't implement an interface. // t is Go interface. // assert that x implements type. - impl := false + var impl bool if nxt, ok := xt.(*NativeType); ok { - impl = nxt.Type.Implements(nt.Type) - } else { - impl = false + // If the underlying native type is reflect.Interface kind, then this has no + // concrete value and should fail. + if nxt.Type.Kind() != reflect.Interface { + impl = nxt.Type.Implements(nt.Type) + } } + if impl { // *xv = *xv *tv = untypedBool(true) From 09f9704fdf4ced234a0d1b075567a5f35d46b7c6 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Feb 2024 09:13:11 -0800 Subject: [PATCH 03/23] added file tests --- gnovm/tests/files/type33.gno | 32 ++++++++++++++++ gnovm/tests/files/type34.gno | 58 +++++++++++++++++++++++++++++ gnovm/tests/files/type35.gno | 30 +++++++++++++++ gnovm/tests/files/type36.gno | 25 +++++++++++++ gnovm/tests/files/type37_native.gno | 44 ++++++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 gnovm/tests/files/type33.gno create mode 100644 gnovm/tests/files/type34.gno create mode 100644 gnovm/tests/files/type35.gno create mode 100644 gnovm/tests/files/type36.gno create mode 100644 gnovm/tests/files/type37_native.gno diff --git a/gnovm/tests/files/type33.gno b/gnovm/tests/files/type33.gno new file mode 100644 index 00000000000..a9791fa0038 --- /dev/null +++ b/gnovm/tests/files/type33.gno @@ -0,0 +1,32 @@ +package main + +type ex int + +func (ex) Error() string { return "" } + +type A interface { + Do(s string) +} + +func main() { + defer func() { + e := recover() + if _, ok := e.(ex); ok { + println("wat") + } else { + println("ok") + } + }() + defer func() { + e := recover() + if _, ok := e.(A); ok { + println("wat") + } else { + println("ok") + } + }() +} + +// Output: +// ok +// ok diff --git a/gnovm/tests/files/type34.gno b/gnovm/tests/files/type34.gno new file mode 100644 index 00000000000..a137d3833ee --- /dev/null +++ b/gnovm/tests/files/type34.gno @@ -0,0 +1,58 @@ +package main + +type Setter interface { + Set(string) +} + +type SetterClone interface { + Set(string) +} + +type ValueSetter struct { + value string +} + +func (s *ValueSetter) Set(value string) { + s.value = value +} + +func cmpSetter(i interface{}) { + if _, ok := i.(Setter); ok { + println("ok") + } else { + println("not ok") + } +} + +func main() { + var ( + i interface{} + setter Setter + setterClone SetterClone + valueSetter ValueSetter + valueSetterPtr *ValueSetter + ) + + cmpSetter(i) + + i = setter + cmpSetter(i) + + setterClone = valueSetterPtr + setter = setterClone + i = setter + cmpSetter(i) + + i = valueSetter + cmpSetter(i) + + i = valueSetterPtr + cmpSetter(i) +} + +// Output: +// not ok +// not ok +// ok +// not ok +// ok diff --git a/gnovm/tests/files/type35.gno b/gnovm/tests/files/type35.gno new file mode 100644 index 00000000000..f548de6c0e1 --- /dev/null +++ b/gnovm/tests/files/type35.gno @@ -0,0 +1,30 @@ +package main + +type A interface { + Do(i int) +} + +type B interface { + Do(i int) +} + +type C struct{} + +func (c C) Do(i int) {} + +func AcceptA(a A) { + AcceptB(a) +} + +func AcceptB(b B) { + if _, ok := b.(A); ok { + println("ok") + } +} + +func main() { + AcceptA(C{}) +} + +// Output: +// ok diff --git a/gnovm/tests/files/type36.gno b/gnovm/tests/files/type36.gno new file mode 100644 index 00000000000..5faa392a703 --- /dev/null +++ b/gnovm/tests/files/type36.gno @@ -0,0 +1,25 @@ +package main + +type A interface { + Do(s string) +} + +func main() { + var v interface{} + v = 9 + + if _, ok := v.(A); !ok { + println(v) + } + + vp := new(int) + *vp = 99 + v = vp + if _, ok := v.(A); !ok { + println(*(v.(*int))) + } +} + +// Output: +// 9 +// 99 diff --git a/gnovm/tests/files/type37_native.gno b/gnovm/tests/files/type37_native.gno new file mode 100644 index 00000000000..ed79ecf2e2e --- /dev/null +++ b/gnovm/tests/files/type37_native.gno @@ -0,0 +1,44 @@ +package main + +import ( + "bytes" + "io" +) + +func main() { + { + var v interface{} + var r io.Reader + r = bytes.NewBuffer([]byte("hello")) + v = r + if _, ok := v.(io.Reader); ok { + println("ok") + } else { + println("not ok") + } + } + { + var v interface{} + var r io.Reader + v = r + if _, ok := v.(io.Reader); ok { + println("ok") + } else { + println("not ok") + } + } + { + var v interface{} + v = bytes.NewBuffer([]byte("hello")) + if _, ok := v.(io.Reader); ok { + println("ok") + } else { + println("not ok") + } + } +} + +// Output: +// ok +// not ok +// ok From 7fce96ed45f17d376a52d07a33fb0bb832d0c9d1 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Feb 2024 09:56:00 -0800 Subject: [PATCH 04/23] code cleanup. make sure typeassert1 handles nil concrete assertions --- gnovm/pkg/gnolang/op_expressions.go | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 1f3110b9fdc..0c49e23f57c 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -204,10 +204,11 @@ func (m *Machine) doOpRef() { func (m *Machine) doOpTypeAssert1() { m.PopExpr() // pop type - t := m.PopValue().GetType() + t := m.PopValue().GetType() // type being asserted + // peek x for re-use - xv := m.PeekValue(1) - xt := xv.T + xv := m.PeekValue(1) // value result / value to assert + xt := xv.T // underlying value's type // xt may be nil, but we need to wait to return because the value of xt that is set // will depend on whether we are trying to assert to an interface or concrete type. @@ -237,7 +238,7 @@ func (m *Machine) doOpTypeAssert1() { // t is Gno interface. // assert that x implements type. - impl := false + var impl bool impl = it.IsImplementedBy(xt) if !impl { // TODO: default panic type? @@ -254,15 +255,14 @@ func (m *Machine) doOpTypeAssert1() { } else if nt, ok := baseOf(t).(*NativeType); ok { // t is Go interface. // assert that x implements type. - var nonConcrete string + nonConcrete := "non-concrete " var impl bool if nxt, ok := xt.(*NativeType); ok { // If the underlying native type is reflect.Interface kind, then this has no // concrete value and should fail. if nxt.Type.Kind() != reflect.Interface { impl = nxt.Type.Implements(nt.Type) - } else { - nonConcrete = "non-concrete " + nonConcrete = "" } } @@ -282,6 +282,12 @@ func (m *Machine) doOpTypeAssert1() { panic("should not happen") } } else { // is concrete assert + if xt == nil { + ex := fmt.Sprintf("nil is not of type %s", t.String()) + m.Panic(typedString(ex)) + return + } + tid := t.TypeID() xtid := xt.TypeID() // assert that x is of type. @@ -308,7 +314,7 @@ func (m *Machine) doOpTypeAssert2() { t := tv.GetType() // type being asserted // peek x for re-use - xv := m.PeekValue(2) // value result + xv := m.PeekValue(2) // value result / value to assert xt := xv.T // underlying value's type // xt may be nil, but we need to wait to return because the value of xt that is set @@ -334,7 +340,7 @@ func (m *Machine) doOpTypeAssert2() { // t is Gno interface. // assert that x implements type. - impl := false + var impl bool impl = it.IsImplementedBy(xt) if impl { // *xv = *xv From 2695b64fbc3bc3bd7ea9914bc502b908aa8c7675 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 27 Feb 2024 09:56:39 -0800 Subject: [PATCH 05/23] copy the tests added for typeassert2 and apply to typeassert1 --- gnovm/tests/files/type33a.gno | 15 +++++++ gnovm/tests/files/type33b.gno | 15 +++++++ gnovm/tests/files/type34a.gno | 62 ++++++++++++++++++++++++++++ gnovm/tests/files/type35a.gno | 29 +++++++++++++ gnovm/tests/files/type36a.gno | 40 ++++++++++++++++++ gnovm/tests/files/type37a_native.gno | 43 +++++++++++++++++++ 6 files changed, 204 insertions(+) create mode 100644 gnovm/tests/files/type33a.gno create mode 100644 gnovm/tests/files/type33b.gno create mode 100644 gnovm/tests/files/type34a.gno create mode 100644 gnovm/tests/files/type35a.gno create mode 100644 gnovm/tests/files/type36a.gno create mode 100644 gnovm/tests/files/type37a_native.gno diff --git a/gnovm/tests/files/type33a.gno b/gnovm/tests/files/type33a.gno new file mode 100644 index 00000000000..f749667be8c --- /dev/null +++ b/gnovm/tests/files/type33a.gno @@ -0,0 +1,15 @@ +package main + +type A interface { + Do(s string) +} + +func main() { + defer func() { + e := recover() + _ = e.(A) + }() +} + +// Error: +// nil doesn't implement main.A diff --git a/gnovm/tests/files/type33b.gno b/gnovm/tests/files/type33b.gno new file mode 100644 index 00000000000..bc33e37b1b5 --- /dev/null +++ b/gnovm/tests/files/type33b.gno @@ -0,0 +1,15 @@ +package main + +type ex int + +func (ex) Error() string { return "" } + +func main() { + defer func() { + e := recover() + _ = e.(ex) + }() +} + +// Error: +// nil is not of type main.ex diff --git a/gnovm/tests/files/type34a.gno b/gnovm/tests/files/type34a.gno new file mode 100644 index 00000000000..8642da5b7cb --- /dev/null +++ b/gnovm/tests/files/type34a.gno @@ -0,0 +1,62 @@ +package main + +type Setter interface { + Set(string) +} + +type SetterClone interface { + Set(string) +} + +type ValueSetter struct { + value string +} + +func (s *ValueSetter) Set(value string) { + s.value = value +} + +func cmpSetter(i interface{}) { + defer func() { + if r := recover(); r != nil { + println(r) + } else { + println("ok") + } + }() + + _ = i.(Setter) +} + +func main() { + var ( + i interface{} + setter Setter + setterClone SetterClone + valueSetter ValueSetter + valueSetterPtr *ValueSetter + ) + + cmpSetter(i) + + i = setter + cmpSetter(i) + + setterClone = valueSetterPtr + setter = setterClone + i = setter + cmpSetter(i) + + i = valueSetter + cmpSetter(i) + + i = valueSetterPtr + cmpSetter(i) +} + +// Output: +// non-concrete interface{} doesn't implement interface{Set func(string)()} +// non-concrete main.Setter doesn't implement interface{Set func(string)()} +// ok +// main.ValueSetter doesn't implement interface{Set func(string)()} +// ok diff --git a/gnovm/tests/files/type35a.gno b/gnovm/tests/files/type35a.gno new file mode 100644 index 00000000000..a4f720d65c6 --- /dev/null +++ b/gnovm/tests/files/type35a.gno @@ -0,0 +1,29 @@ +package main + +type A interface { + Do(i int) +} + +type B interface { + Do(i int) +} + +type C struct{} + +func (c C) Do(i int) {} + +func AcceptA(a A) { + AcceptB(a) +} + +func AcceptB(b B) { + _ = b.(A) + println("ok") +} + +func main() { + AcceptA(C{}) +} + +// Output: +// ok diff --git a/gnovm/tests/files/type36a.gno b/gnovm/tests/files/type36a.gno new file mode 100644 index 00000000000..f4b925cfeee --- /dev/null +++ b/gnovm/tests/files/type36a.gno @@ -0,0 +1,40 @@ +package main + +type A interface { + Do(s string) +} + +func test1() { + defer func() { + if r := recover(); r != nil { + println(r) + } + }() + + var v interface{} + v = 9 + _ = v.(A) +} + +func test2() { + defer func() { + if r := recover(); r != nil { + println(r) + } + }() + + var v interface{} + vp := new(int) + *vp = 99 + v = vp + _ = v.(A) +} + +func main() { + test1() + test2() +} + +// Output: +// int doesn't implement interface{Do func(string)()} +// *int doesn't implement interface{Do func(string)()} diff --git a/gnovm/tests/files/type37a_native.gno b/gnovm/tests/files/type37a_native.gno new file mode 100644 index 00000000000..14a80ff4951 --- /dev/null +++ b/gnovm/tests/files/type37a_native.gno @@ -0,0 +1,43 @@ +package main + +import ( + "bytes" + "io" +) + +func testImpl(v interface{}) { + defer func() { + if r := recover(); r != nil { + println(r) + } + }() + + _ = v.(io.Reader) + println("ok") +} + +func main() { + { + var v interface{} + var r io.Reader + r = bytes.NewBuffer([]byte("hello")) + v = r + testImpl(v) + } + { + var v interface{} + var r io.Reader + v = r + testImpl(v) + } + { + var v interface{} + v = bytes.NewBuffer([]byte("hello")) + testImpl(v) + } +} + +// Output: +// ok +// non-concrete gonative{io.Reader} doesn't implement gonative{io.Reader} +// ok From 347db5de435cdf836e043e550c880d6d5b907858 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 29 Feb 2024 10:15:17 -0800 Subject: [PATCH 06/23] removed unnecessary conditional --- gnovm/pkg/gnolang/op_expressions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 0c49e23f57c..9d95c107268 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -389,7 +389,7 @@ func (m *Machine) doOpTypeAssert2() { // assert that x is of type. same := tid == xtid - if same && xt != nil { + if same { // *xv = *xv *tv = untypedBool(true) } else { From e34a322859250dc4d440a0fa19a88d448112ab2f Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 27 Mar 2024 16:16:23 -0700 Subject: [PATCH 07/23] don't allow max cycle configuration --- gno.land/cmd/gnoland/start.go | 10 +--------- gno.land/pkg/gnoland/app.go | 5 ++--- gno.land/pkg/gnoland/node_inmemory.go | 9 +++------ gno.land/pkg/sdk/vm/common_test.go | 2 +- gno.land/pkg/sdk/vm/keeper.go | 2 +- 5 files changed, 8 insertions(+), 20 deletions(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 2b1757706f8..7fa7de32e5c 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -36,7 +36,6 @@ type startCfg struct { chainID string genesisRemote string dataDir string - genesisMaxVMCycles int64 config string txEventStoreType string @@ -125,13 +124,6 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { "replacement for '%%REMOTE%%' in genesis", ) - fs.Int64Var( - &c.genesisMaxVMCycles, - "genesis-max-vm-cycles", - 10_000_000, - "set maximum allowed vm cycles per operation. Zero means no limit.", - ) - fs.StringVar( &c.config, flagConfigFlag, @@ -254,7 +246,7 @@ func execStart(c *startCfg, io commands.IO) error { cfg.TxEventStore = txEventStoreCfg // Create application and node. - gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles) + gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger) if err != nil { return fmt.Errorf("error in creating new app: %w", err) } diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index cc15f74134e..86fb6321386 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -31,7 +31,6 @@ type AppOptions struct { GnoRootDir string SkipFailingGenesisTxs bool Logger *slog.Logger - MaxCycles int64 } func NewAppOptions() *AppOptions { @@ -78,7 +77,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") - vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) + vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir) // Set InitChainer baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.SkipFailingGenesisTxs)) @@ -123,7 +122,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { } // NewApp creates the GnoLand application. -func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger, maxCycles int64) (abci.Application, error) { +func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger) (abci.Application, error) { var err error cfg := NewAppOptions() diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index 89f222738d0..d8dc3caa485 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -25,7 +25,6 @@ type InMemoryNodeConfig struct { Genesis *bft.GenesisDoc TMConfig *tmcfg.Config SkipFailingGenesisTxs bool - GenesisMaxVMCycles int64 } // NewMockedPrivValidator generate a new key @@ -79,10 +78,9 @@ func NewDefaultInMemoryNodeConfig(rootdir string) *InMemoryNodeConfig { } return &InMemoryNodeConfig{ - PrivValidator: pv, - TMConfig: tm, - Genesis: genesis, - GenesisMaxVMCycles: 10_000_000, + PrivValidator: pv, + TMConfig: tm, + Genesis: genesis, } } @@ -115,7 +113,6 @@ func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node, Logger: logger, GnoRootDir: cfg.TMConfig.RootDir, SkipFailingGenesisTxs: cfg.SkipFailingGenesisTxs, - MaxCycles: cfg.GenesisMaxVMCycles, DB: memdb.NewMemDB(), }) if err != nil { diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index b65757da403..ec14ae8515b 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -39,7 +39,7 @@ func setupTestEnv() testEnv { acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount) bank := bankm.NewBankKeeper(acck) stdlibsDir := filepath.Join("..", "..", "..", "..", "gnovm", "stdlibs") - vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, stdlibsDir, 10_000_000) + vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, stdlibsDir) vmk.Initialize(ms.MultiCacheWrap()) diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 67710be620c..2e61b02a56d 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -22,6 +22,7 @@ import ( const ( maxAllocTx = 500 * 1000 * 1000 maxAllocQuery = 1500 * 1000 * 1000 // higher limit for queries + maxCycles = 10_000_000 ) // vm.VMKeeperI defines a module interface that supports Gno @@ -55,7 +56,6 @@ func NewVMKeeper( acck auth.AccountKeeper, bank bank.BankKeeper, stdlibsDir string, - maxCycles int64, ) *VMKeeper { // TODO: create an Options struct to avoid too many constructor parameters vmk := &VMKeeper{ From 2866d9787ecbbb50f6aae821524be913cd2ff4db Mon Sep 17 00:00:00 2001 From: deelawn Date: Wed, 27 Mar 2024 16:17:48 -0700 Subject: [PATCH 08/23] Revert "don't allow max cycle configuration" This reverts commit e34a322859250dc4d440a0fa19a88d448112ab2f. --- gno.land/cmd/gnoland/start.go | 10 +++++++++- gno.land/pkg/gnoland/app.go | 5 +++-- gno.land/pkg/gnoland/node_inmemory.go | 9 ++++++--- gno.land/pkg/sdk/vm/common_test.go | 2 +- gno.land/pkg/sdk/vm/keeper.go | 2 +- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 7fa7de32e5c..2b1757706f8 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -36,6 +36,7 @@ type startCfg struct { chainID string genesisRemote string dataDir string + genesisMaxVMCycles int64 config string txEventStoreType string @@ -124,6 +125,13 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { "replacement for '%%REMOTE%%' in genesis", ) + fs.Int64Var( + &c.genesisMaxVMCycles, + "genesis-max-vm-cycles", + 10_000_000, + "set maximum allowed vm cycles per operation. Zero means no limit.", + ) + fs.StringVar( &c.config, flagConfigFlag, @@ -246,7 +254,7 @@ func execStart(c *startCfg, io commands.IO) error { cfg.TxEventStore = txEventStoreCfg // Create application and node. - gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger) + gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles) if err != nil { return fmt.Errorf("error in creating new app: %w", err) } diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 86fb6321386..cc15f74134e 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -31,6 +31,7 @@ type AppOptions struct { GnoRootDir string SkipFailingGenesisTxs bool Logger *slog.Logger + MaxCycles int64 } func NewAppOptions() *AppOptions { @@ -77,7 +78,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") - vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir) + vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.SkipFailingGenesisTxs)) @@ -122,7 +123,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { } // NewApp creates the GnoLand application. -func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger) (abci.Application, error) { +func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger, maxCycles int64) (abci.Application, error) { var err error cfg := NewAppOptions() diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index d8dc3caa485..89f222738d0 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -25,6 +25,7 @@ type InMemoryNodeConfig struct { Genesis *bft.GenesisDoc TMConfig *tmcfg.Config SkipFailingGenesisTxs bool + GenesisMaxVMCycles int64 } // NewMockedPrivValidator generate a new key @@ -78,9 +79,10 @@ func NewDefaultInMemoryNodeConfig(rootdir string) *InMemoryNodeConfig { } return &InMemoryNodeConfig{ - PrivValidator: pv, - TMConfig: tm, - Genesis: genesis, + PrivValidator: pv, + TMConfig: tm, + Genesis: genesis, + GenesisMaxVMCycles: 10_000_000, } } @@ -113,6 +115,7 @@ func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node, Logger: logger, GnoRootDir: cfg.TMConfig.RootDir, SkipFailingGenesisTxs: cfg.SkipFailingGenesisTxs, + MaxCycles: cfg.GenesisMaxVMCycles, DB: memdb.NewMemDB(), }) if err != nil { diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index ec14ae8515b..b65757da403 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -39,7 +39,7 @@ func setupTestEnv() testEnv { acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount) bank := bankm.NewBankKeeper(acck) stdlibsDir := filepath.Join("..", "..", "..", "..", "gnovm", "stdlibs") - vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, stdlibsDir) + vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, stdlibsDir, 10_000_000) vmk.Initialize(ms.MultiCacheWrap()) diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 2e61b02a56d..67710be620c 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -22,7 +22,6 @@ import ( const ( maxAllocTx = 500 * 1000 * 1000 maxAllocQuery = 1500 * 1000 * 1000 // higher limit for queries - maxCycles = 10_000_000 ) // vm.VMKeeperI defines a module interface that supports Gno @@ -56,6 +55,7 @@ func NewVMKeeper( acck auth.AccountKeeper, bank bank.BankKeeper, stdlibsDir string, + maxCycles int64, ) *VMKeeper { // TODO: create an Options struct to avoid too many constructor parameters vmk := &VMKeeper{ From 1db1c4c7e556daa4443f70ee7675e702794c6f64 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 8 Apr 2024 12:47:25 -0700 Subject: [PATCH 09/23] variable rename --- gnovm/pkg/gnolang/op_expressions.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 9d95c107268..648241a17e4 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -255,14 +255,14 @@ func (m *Machine) doOpTypeAssert1() { } else if nt, ok := baseOf(t).(*NativeType); ok { // t is Go interface. // assert that x implements type. - nonConcrete := "non-concrete " + errPrefix := "non-concrete " var impl bool if nxt, ok := xt.(*NativeType); ok { // If the underlying native type is reflect.Interface kind, then this has no // concrete value and should fail. if nxt.Type.Kind() != reflect.Interface { impl = nxt.Type.Implements(nt.Type) - nonConcrete = "" + errPrefix = "" } } @@ -270,7 +270,7 @@ func (m *Machine) doOpTypeAssert1() { // TODO: default panic type? ex := fmt.Sprintf( "%s%s doesn't implement %s", - nonConcrete, + errPrefix, xt.String(), nt.String()) m.Panic(typedString(ex)) From d9851daf05ff2c2e5a76f89e39ab7fe89c16ee21 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 13:09:00 -0700 Subject: [PATCH 10/23] added basic wildcard matching for filetest errors --- gnovm/tests/file.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 70bed4eda50..15a6c487ebd 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -251,7 +251,20 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { default: errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc)) } - if errstr != errWanted { + + matches := errstr == errWanted + if strings.Contains(errWanted, "*") { + if errWanted == "*" { + matches = true + } else if len(errWanted) > 1 && errWanted[0] == '*' && errWanted[len(errWanted)-1] == '*' { + matches = strings.Contains(errstr, errWanted[1:len(errWanted)-1]) + } else if errWanted[0] == '*' { + matches = strings.HasSuffix(errstr, errWanted[1:]) + } else if errWanted[len(errWanted)-1] == '*' { + matches = strings.HasPrefix(errstr, errWanted[:len(errWanted)-1]) + } + } + if !matches { panic(fmt.Sprintf("fail on %s: got %q, want: %q", path, errstr, errWanted)) } From fc58bebfda6a947333656800be06d6578fd3dcd6 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 13:10:49 -0700 Subject: [PATCH 11/23] only allow type assertions on interface types --- gnovm/pkg/gnolang/preprocess.go | 10 ++++++++++ gnovm/tests/files/type38.gno | 18 ++++++++++++++++++ gnovm/tests/files/type38a.gno | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 gnovm/tests/files/type38.gno create mode 100644 gnovm/tests/files/type38a.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 1d215e4d94b..3b4b87d63d0 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1234,6 +1234,16 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } // ExprStmt of form `x.()`, // or special case form `c, ok := x.()`. + t := evalStaticTypeOf(store, last, n.X) + if _, ok := baseOf(t).(*InterfaceType); !ok { + panic( + fmt.Sprintf( + "invalid operation: %s (variable of type %s) is not an interface", + n.X.String(), + t.String(), + ), + ) + } evalStaticType(store, last, n.Type) // TRANS_LEAVE ----------------------- diff --git a/gnovm/tests/files/type38.gno b/gnovm/tests/files/type38.gno new file mode 100644 index 00000000000..eca8d5f0dd7 --- /dev/null +++ b/gnovm/tests/files/type38.gno @@ -0,0 +1,18 @@ +package main + +type ex int + +func (ex) Error() string { return "" } + +type i interface { + Error() string +} + +func main() { + r := []int(nil) + e := r.(ex) + println(e) +} + +// Error: +// *invalid operation: r (variable of type []int) is not an interface* diff --git a/gnovm/tests/files/type38a.gno b/gnovm/tests/files/type38a.gno new file mode 100644 index 00000000000..c682724576e --- /dev/null +++ b/gnovm/tests/files/type38a.gno @@ -0,0 +1,18 @@ +package main + +type ex int + +func (ex) Error() string { return "" } + +type i interface { + Error() string +} + +func main() { + r := []int(nil) + e, ok := r.(ex) + println(e, ok) +} + +// Error: +// *invalid operation: r (variable of type []int) is not an interface* From 5074ebaaae4cbcc2f29ca1d376794b1dabf213da Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 13:17:39 -0700 Subject: [PATCH 12/23] don't allow type assertions of the blank identifer --- gnovm/pkg/gnolang/preprocess.go | 8 ++++++++ gnovm/tests/files/type39.gno | 15 +++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 gnovm/tests/files/type39.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 3b4b87d63d0..af030a7dbd0 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -8,6 +8,8 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) +const blankIdentifer string = "_" + // In the case of a *FileSet, some declaration steps have to happen // in a restricted parallel way across all the files. // Anything predefined or preprocessed here get skipped during the Preprocess @@ -1232,6 +1234,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if n.Type == nil { panic("should not happen") } + + // Type assertions on the blank identifier are illegal. + if string(n.X.(*NameExpr).Name) == blankIdentifer { + panic("cannot use _ as a value or type") + } + // ExprStmt of form `x.()`, // or special case form `c, ok := x.()`. t := evalStaticTypeOf(store, last, n.X) diff --git a/gnovm/tests/files/type39.gno b/gnovm/tests/files/type39.gno new file mode 100644 index 00000000000..387772887fa --- /dev/null +++ b/gnovm/tests/files/type39.gno @@ -0,0 +1,15 @@ +package main + +type ex int + +func (ex) Error() string { return "" } + +func main() { + defer func() { + r := _.(ex) + println(r) + }() +} + +// Error: +// *cannot use _ as a value or type* From 2efb7b82fec362b7c5649f43bd1e056b90e2e1e9 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 13:37:49 -0700 Subject: [PATCH 13/23] panic on nil interface assertions --- gnovm/pkg/gnolang/op_expressions.go | 4 ++-- gnovm/tests/files/type40.gno | 15 +++++++++++++++ gnovm/tests/files/type40a.gno | 13 +++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 gnovm/tests/files/type40.gno create mode 100644 gnovm/tests/files/type40a.gno diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 648241a17e4..94d1c62e1d7 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -216,9 +216,9 @@ func (m *Machine) doOpTypeAssert1() { // returns a bare TypedValue{}. if t.Kind() == InterfaceKind { // is interface assert - if xt == nil { + if xt == nil || xv.IsNilInterface() { // TODO: default panic type? - ex := fmt.Sprintf("nil doesn't implement %s", t.String()) + ex := fmt.Sprintf("interface conversion: interface is nil, not %s", t.String()) m.Panic(typedString(ex)) return } diff --git a/gnovm/tests/files/type40.gno b/gnovm/tests/files/type40.gno new file mode 100644 index 00000000000..0441bf83437 --- /dev/null +++ b/gnovm/tests/files/type40.gno @@ -0,0 +1,15 @@ +package main + +type A interface { + Do(s string) +} + +func main() { + defer func() { + e := recover() + _ = e.(A) + }() +} + +// Error: +// interface conversion: interface is nil, not main.A diff --git a/gnovm/tests/files/type40a.gno b/gnovm/tests/files/type40a.gno new file mode 100644 index 00000000000..041034e4bd0 --- /dev/null +++ b/gnovm/tests/files/type40a.gno @@ -0,0 +1,13 @@ +package main + +type A interface { + Do(s string) +} + +func main() { + var a A + _ = a.(A) +} + +// Error: +// interface conversion: interface is nil, not main.A From ce6b12dc132b3e3372c8b4a0ecc7b59eab332cd7 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 13:40:54 -0700 Subject: [PATCH 14/23] verify expression type --- gnovm/pkg/gnolang/preprocess.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index af030a7dbd0..e79919c8dce 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1236,7 +1236,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } // Type assertions on the blank identifier are illegal. - if string(n.X.(*NameExpr).Name) == blankIdentifer { + if nx, ok := n.X.(*NameExpr); ok && string(nx.Name) == blankIdentifer { panic("cannot use _ as a value or type") } From bd10bac3d79ffa8fa53f2882b10980c752897250 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 13:42:07 -0700 Subject: [PATCH 15/23] comment --- gnovm/tests/file.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 15a6c487ebd..cdd77ee5b25 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -252,6 +252,8 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc)) } + // Matching on long panic strings from the preprocessor can be hard/annoying. This allows support for a + // wildcard match for the entire string, prefix, suffix, or both. matches := errstr == errWanted if strings.Contains(errWanted, "*") { if errWanted == "*" { From 5867ba925c9cd65f321156065dc21f17ea74b1fd Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 13:48:29 -0700 Subject: [PATCH 16/23] rename tests added --- gnovm/tests/files/{type32.gno => typeassert1.gno} | 0 gnovm/tests/files/{type40.gno => typeassert10.gno} | 0 gnovm/tests/files/{type40a.gno => typeassert10a.gno} | 0 gnovm/tests/files/{type33.gno => typeassert2.gno} | 0 gnovm/tests/files/{type33a.gno => typeassert2a.gno} | 0 gnovm/tests/files/{type33b.gno => typeassert3.gno} | 0 gnovm/tests/files/{type34.gno => typeassert4.gno} | 0 gnovm/tests/files/{type34a.gno => typeassert4a.gno} | 0 gnovm/tests/files/{type35.gno => typeassert5.gno} | 0 gnovm/tests/files/{type35a.gno => typeassert5a.gno} | 0 gnovm/tests/files/{type36.gno => typeassert6.gno} | 0 gnovm/tests/files/{type36a.gno => typeassert6a.gno} | 0 gnovm/tests/files/{type37_native.gno => typeassert7_native.gno} | 0 gnovm/tests/files/{type37a_native.gno => typeassert7a_native.gno} | 0 gnovm/tests/files/{type38.gno => typeassert8.gno} | 0 gnovm/tests/files/{type38a.gno => typeassert8a.gno} | 0 gnovm/tests/files/{type39.gno => typeassert9.gno} | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename gnovm/tests/files/{type32.gno => typeassert1.gno} (100%) rename gnovm/tests/files/{type40.gno => typeassert10.gno} (100%) rename gnovm/tests/files/{type40a.gno => typeassert10a.gno} (100%) rename gnovm/tests/files/{type33.gno => typeassert2.gno} (100%) rename gnovm/tests/files/{type33a.gno => typeassert2a.gno} (100%) rename gnovm/tests/files/{type33b.gno => typeassert3.gno} (100%) rename gnovm/tests/files/{type34.gno => typeassert4.gno} (100%) rename gnovm/tests/files/{type34a.gno => typeassert4a.gno} (100%) rename gnovm/tests/files/{type35.gno => typeassert5.gno} (100%) rename gnovm/tests/files/{type35a.gno => typeassert5a.gno} (100%) rename gnovm/tests/files/{type36.gno => typeassert6.gno} (100%) rename gnovm/tests/files/{type36a.gno => typeassert6a.gno} (100%) rename gnovm/tests/files/{type37_native.gno => typeassert7_native.gno} (100%) rename gnovm/tests/files/{type37a_native.gno => typeassert7a_native.gno} (100%) rename gnovm/tests/files/{type38.gno => typeassert8.gno} (100%) rename gnovm/tests/files/{type38a.gno => typeassert8a.gno} (100%) rename gnovm/tests/files/{type39.gno => typeassert9.gno} (100%) diff --git a/gnovm/tests/files/type32.gno b/gnovm/tests/files/typeassert1.gno similarity index 100% rename from gnovm/tests/files/type32.gno rename to gnovm/tests/files/typeassert1.gno diff --git a/gnovm/tests/files/type40.gno b/gnovm/tests/files/typeassert10.gno similarity index 100% rename from gnovm/tests/files/type40.gno rename to gnovm/tests/files/typeassert10.gno diff --git a/gnovm/tests/files/type40a.gno b/gnovm/tests/files/typeassert10a.gno similarity index 100% rename from gnovm/tests/files/type40a.gno rename to gnovm/tests/files/typeassert10a.gno diff --git a/gnovm/tests/files/type33.gno b/gnovm/tests/files/typeassert2.gno similarity index 100% rename from gnovm/tests/files/type33.gno rename to gnovm/tests/files/typeassert2.gno diff --git a/gnovm/tests/files/type33a.gno b/gnovm/tests/files/typeassert2a.gno similarity index 100% rename from gnovm/tests/files/type33a.gno rename to gnovm/tests/files/typeassert2a.gno diff --git a/gnovm/tests/files/type33b.gno b/gnovm/tests/files/typeassert3.gno similarity index 100% rename from gnovm/tests/files/type33b.gno rename to gnovm/tests/files/typeassert3.gno diff --git a/gnovm/tests/files/type34.gno b/gnovm/tests/files/typeassert4.gno similarity index 100% rename from gnovm/tests/files/type34.gno rename to gnovm/tests/files/typeassert4.gno diff --git a/gnovm/tests/files/type34a.gno b/gnovm/tests/files/typeassert4a.gno similarity index 100% rename from gnovm/tests/files/type34a.gno rename to gnovm/tests/files/typeassert4a.gno diff --git a/gnovm/tests/files/type35.gno b/gnovm/tests/files/typeassert5.gno similarity index 100% rename from gnovm/tests/files/type35.gno rename to gnovm/tests/files/typeassert5.gno diff --git a/gnovm/tests/files/type35a.gno b/gnovm/tests/files/typeassert5a.gno similarity index 100% rename from gnovm/tests/files/type35a.gno rename to gnovm/tests/files/typeassert5a.gno diff --git a/gnovm/tests/files/type36.gno b/gnovm/tests/files/typeassert6.gno similarity index 100% rename from gnovm/tests/files/type36.gno rename to gnovm/tests/files/typeassert6.gno diff --git a/gnovm/tests/files/type36a.gno b/gnovm/tests/files/typeassert6a.gno similarity index 100% rename from gnovm/tests/files/type36a.gno rename to gnovm/tests/files/typeassert6a.gno diff --git a/gnovm/tests/files/type37_native.gno b/gnovm/tests/files/typeassert7_native.gno similarity index 100% rename from gnovm/tests/files/type37_native.gno rename to gnovm/tests/files/typeassert7_native.gno diff --git a/gnovm/tests/files/type37a_native.gno b/gnovm/tests/files/typeassert7a_native.gno similarity index 100% rename from gnovm/tests/files/type37a_native.gno rename to gnovm/tests/files/typeassert7a_native.gno diff --git a/gnovm/tests/files/type38.gno b/gnovm/tests/files/typeassert8.gno similarity index 100% rename from gnovm/tests/files/type38.gno rename to gnovm/tests/files/typeassert8.gno diff --git a/gnovm/tests/files/type38a.gno b/gnovm/tests/files/typeassert8a.gno similarity index 100% rename from gnovm/tests/files/type38a.gno rename to gnovm/tests/files/typeassert8a.gno diff --git a/gnovm/tests/files/type39.gno b/gnovm/tests/files/typeassert9.gno similarity index 100% rename from gnovm/tests/files/type39.gno rename to gnovm/tests/files/typeassert9.gno From 6196fdc1aa79248bcdd8ccf331edb18dbd8a3157 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 14:18:45 -0700 Subject: [PATCH 17/23] account for native interface types in type assertion preprocessing --- gnovm/pkg/gnolang/preprocess.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 4da26179c36..0a0d6157512 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1245,7 +1245,15 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // ExprStmt of form `x.()`, // or special case form `c, ok := x.()`. t := evalStaticTypeOf(store, last, n.X) - if _, ok := baseOf(t).(*InterfaceType); !ok { + baseType := baseOf(t) // The base type of the asserted value must be an interface. + switch bt := baseType.(type) { + case *InterfaceType: + break + case *NativeType: + if bt.Type.Kind() == reflect.Interface { + break + } + default: panic( fmt.Sprintf( "invalid operation: %s (variable of type %s) is not an interface", @@ -1254,6 +1262,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { ), ) } + evalStaticType(store, last, n.Type) // TRANS_LEAVE ----------------------- From bd65ea29648f92800a587405224a829c44b6760c Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 14:23:15 -0700 Subject: [PATCH 18/23] restore moved test --- gnovm/tests/files/{typeassert1.gno => type32.gno} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gnovm/tests/files/{typeassert1.gno => type32.gno} (100%) diff --git a/gnovm/tests/files/typeassert1.gno b/gnovm/tests/files/type32.gno similarity index 100% rename from gnovm/tests/files/typeassert1.gno rename to gnovm/tests/files/type32.gno From ec58faa4531954231796ad11d689c298dfe86feb Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 14:31:06 -0700 Subject: [PATCH 19/23] removed dup tests and fixed some --- .../files/{typeassert10a.gno => typeassert1.gno} | 0 gnovm/tests/files/typeassert10.gno | 15 --------------- gnovm/tests/files/typeassert2a.gno | 2 +- gnovm/tests/files/typeassert3.gno | 2 +- gnovm/tests/files/typeassert9.gno | 15 --------------- 5 files changed, 2 insertions(+), 32 deletions(-) rename gnovm/tests/files/{typeassert10a.gno => typeassert1.gno} (100%) delete mode 100644 gnovm/tests/files/typeassert10.gno delete mode 100644 gnovm/tests/files/typeassert9.gno diff --git a/gnovm/tests/files/typeassert10a.gno b/gnovm/tests/files/typeassert1.gno similarity index 100% rename from gnovm/tests/files/typeassert10a.gno rename to gnovm/tests/files/typeassert1.gno diff --git a/gnovm/tests/files/typeassert10.gno b/gnovm/tests/files/typeassert10.gno deleted file mode 100644 index 0441bf83437..00000000000 --- a/gnovm/tests/files/typeassert10.gno +++ /dev/null @@ -1,15 +0,0 @@ -package main - -type A interface { - Do(s string) -} - -func main() { - defer func() { - e := recover() - _ = e.(A) - }() -} - -// Error: -// interface conversion: interface is nil, not main.A diff --git a/gnovm/tests/files/typeassert2a.gno b/gnovm/tests/files/typeassert2a.gno index f749667be8c..0441bf83437 100644 --- a/gnovm/tests/files/typeassert2a.gno +++ b/gnovm/tests/files/typeassert2a.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// nil doesn't implement main.A +// interface conversion: interface is nil, not main.A diff --git a/gnovm/tests/files/typeassert3.gno b/gnovm/tests/files/typeassert3.gno index bc33e37b1b5..917a0f76326 100644 --- a/gnovm/tests/files/typeassert3.gno +++ b/gnovm/tests/files/typeassert3.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// nil is not of type main.ex +// *cannot use _ as a value or type* diff --git a/gnovm/tests/files/typeassert9.gno b/gnovm/tests/files/typeassert9.gno deleted file mode 100644 index 387772887fa..00000000000 --- a/gnovm/tests/files/typeassert9.gno +++ /dev/null @@ -1,15 +0,0 @@ -package main - -type ex int - -func (ex) Error() string { return "" } - -func main() { - defer func() { - r := _.(ex) - println(r) - }() -} - -// Error: -// *cannot use _ as a value or type* From c52c2437f5e86083125a3b257426e45510dd7572 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 15:16:55 -0700 Subject: [PATCH 20/23] fixed error message --- gnovm/pkg/gnolang/preprocess.go | 2 +- gnovm/tests/files/typeassert3.gno | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 0a0d6157512..a4e28438690 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1239,7 +1239,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Type assertions on the blank identifier are illegal. if nx, ok := n.X.(*NameExpr); ok && string(nx.Name) == blankIdentifer { - panic("cannot use _ as a value or type") + panic("cannot use _ as value or type") } // ExprStmt of form `x.()`, diff --git a/gnovm/tests/files/typeassert3.gno b/gnovm/tests/files/typeassert3.gno index 917a0f76326..3aded224b33 100644 --- a/gnovm/tests/files/typeassert3.gno +++ b/gnovm/tests/files/typeassert3.gno @@ -6,10 +6,10 @@ func (ex) Error() string { return "" } func main() { defer func() { - e := recover() - _ = e.(ex) + r := _.(ex) + println(r) }() } // Error: -// *cannot use _ as a value or type* +// *cannot use _ as value or type* From 3301ffef7cc1a8d893d8390949dd97e652359c8b Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 16 Apr 2024 15:31:31 -0700 Subject: [PATCH 21/23] updated tests error message --- gnovm/tests/files/typeassert4a.gno | 4 ++-- gnovm/tests/files/typeassert7a_native.gno | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gnovm/tests/files/typeassert4a.gno b/gnovm/tests/files/typeassert4a.gno index 8642da5b7cb..46d3728de1f 100644 --- a/gnovm/tests/files/typeassert4a.gno +++ b/gnovm/tests/files/typeassert4a.gno @@ -55,8 +55,8 @@ func main() { } // Output: -// non-concrete interface{} doesn't implement interface{Set func(string)()} -// non-concrete main.Setter doesn't implement interface{Set func(string)()} +// interface conversion: interface is nil, not main.Setter +// interface conversion: interface is nil, not main.Setter // ok // main.ValueSetter doesn't implement interface{Set func(string)()} // ok diff --git a/gnovm/tests/files/typeassert7a_native.gno b/gnovm/tests/files/typeassert7a_native.gno index 14a80ff4951..cafb27b6a6b 100644 --- a/gnovm/tests/files/typeassert7a_native.gno +++ b/gnovm/tests/files/typeassert7a_native.gno @@ -39,5 +39,5 @@ func main() { // Output: // ok -// non-concrete gonative{io.Reader} doesn't implement gonative{io.Reader} +// interface conversion: interface is nil, not gonative{io.Reader} // ok From cb2a92b32e730009df448f6c269707f0d7414ca4 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 18 Apr 2024 13:02:08 -0700 Subject: [PATCH 22/23] undid stupid idea --- gnovm/tests/file.go | 16 +--------------- gnovm/tests/files/typeassert3.gno | 2 +- gnovm/tests/files/typeassert8.gno | 2 +- gnovm/tests/files/typeassert8a.gno | 2 +- 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index cdd77ee5b25..f04c788bb91 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -252,21 +252,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc)) } - // Matching on long panic strings from the preprocessor can be hard/annoying. This allows support for a - // wildcard match for the entire string, prefix, suffix, or both. - matches := errstr == errWanted - if strings.Contains(errWanted, "*") { - if errWanted == "*" { - matches = true - } else if len(errWanted) > 1 && errWanted[0] == '*' && errWanted[len(errWanted)-1] == '*' { - matches = strings.Contains(errstr, errWanted[1:len(errWanted)-1]) - } else if errWanted[0] == '*' { - matches = strings.HasSuffix(errstr, errWanted[1:]) - } else if errWanted[len(errWanted)-1] == '*' { - matches = strings.HasPrefix(errstr, errWanted[:len(errWanted)-1]) - } - } - if !matches { + if errstr != errWanted { panic(fmt.Sprintf("fail on %s: got %q, want: %q", path, errstr, errWanted)) } diff --git a/gnovm/tests/files/typeassert3.gno b/gnovm/tests/files/typeassert3.gno index 3aded224b33..83bedd503f7 100644 --- a/gnovm/tests/files/typeassert3.gno +++ b/gnovm/tests/files/typeassert3.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// *cannot use _ as value or type* +// main/files/typeassert3.gno:9: cannot use _ as value or type diff --git a/gnovm/tests/files/typeassert8.gno b/gnovm/tests/files/typeassert8.gno index eca8d5f0dd7..b744afc8703 100644 --- a/gnovm/tests/files/typeassert8.gno +++ b/gnovm/tests/files/typeassert8.gno @@ -15,4 +15,4 @@ func main() { } // Error: -// *invalid operation: r (variable of type []int) is not an interface* +// main/files/typeassert8.gno:13: invalid operation: r (variable of type []int) is not an interface diff --git a/gnovm/tests/files/typeassert8a.gno b/gnovm/tests/files/typeassert8a.gno index c682724576e..a32aadc7679 100644 --- a/gnovm/tests/files/typeassert8a.gno +++ b/gnovm/tests/files/typeassert8a.gno @@ -15,4 +15,4 @@ func main() { } // Error: -// *invalid operation: r (variable of type []int) is not an interface* +// main/files/typeassert8a.gno:13: invalid operation: r (variable of type []int) is not an interface From 7685853a5b61828231e2448c9603ae443a198ee2 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 23 Apr 2024 13:14:21 -0700 Subject: [PATCH 23/23] added test --- gnovm/tests/files/typeassert9.gno | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 gnovm/tests/files/typeassert9.gno diff --git a/gnovm/tests/files/typeassert9.gno b/gnovm/tests/files/typeassert9.gno new file mode 100644 index 00000000000..d9d5bad55af --- /dev/null +++ b/gnovm/tests/files/typeassert9.gno @@ -0,0 +1,20 @@ +package main + +// First interface +type Reader interface { + Read() string +} + +// Second interface +type Writer interface { + Write() string +} + +func main() { + var reader Reader + + _ = reader.(Writer) +} + +// Error: +// interface conversion: interface is nil, not main.Writer