From 7e2cb6950bb48e6cffc6d517153d9eb79aeb2f76 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Thu, 6 Apr 2023 15:31:25 +0200 Subject: [PATCH] chore: recoverable panic in native functions Fix #663 Some panic invoked in native functions should be recoverable from gno code. To do that, `panic` is replaced with `machine.Panic()` which checks first beyond the call stack if there's a recover, before panic-ing. --- gnovm/stdlibs/stdlibs.go | 11 +++++++---- gnovm/tests/files/std10.gno | 14 ++++++++++++++ gnovm/tests/files/std11.gno | 14 ++++++++++++++ gnovm/tests/files/std9.gno | 14 ++++++++++++++ gnovm/tests/imports.go | 7 +++++-- 5 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 gnovm/tests/files/std10.gno create mode 100644 gnovm/tests/files/std11.gno create mode 100644 gnovm/tests/files/std9.gno diff --git a/gnovm/stdlibs/stdlibs.go b/gnovm/stdlibs/stdlibs.go index 89329aeaf4b..95803558d06 100644 --- a/gnovm/stdlibs/stdlibs.go +++ b/gnovm/stdlibs/stdlibs.go @@ -152,7 +152,8 @@ func InjectPackage(store gno.Store, pn *gno.PackageNode) { func(m *gno.Machine) { isOrigin := len(m.Frames) == 2 if !isOrigin { - panic("invalid non-origin call") + m.Panic(typedString("invalid non-origin call")) + return } }, ) @@ -314,13 +315,15 @@ func InjectPackage(store gno.Store, pn *gno.PackageNode) { arg0 := m.LastBlock().GetParams1().TV n := arg0.GetInt() if n <= 0 { - panic("GetCallerAt requires positive arg") + m.Panic(typedString("GetCallerAt requires positive arg")) + return } if n > m.NumFrames() { // NOTE: the last frame's LastPackage // is set to the original non-frame // package, so need this check. - panic("frame not found") + m.Panic(typedString("frame not found")) + return } var pkgAddr string if n == m.NumFrames() { @@ -426,7 +429,7 @@ func InjectPackage(store gno.Store, pn *gno.PackageNode) { } b32, err := bech32.ConvertAndEncode(prefix, bz) if err != nil { - panic(err) + panic(err) // should not happen } res0 := gno.Go2GnoValue( m.Alloc, diff --git a/gnovm/tests/files/std10.gno b/gnovm/tests/files/std10.gno new file mode 100644 index 00000000000..7caf534c5ca --- /dev/null +++ b/gnovm/tests/files/std10.gno @@ -0,0 +1,14 @@ +package main + +import "std" + +func main() { + defer func() { + // assert panic is recoverable + println(recover()) + }() + std.GetCallerAt(0) +} + +// Output: +// GetCallerAt requires positive arg diff --git a/gnovm/tests/files/std11.gno b/gnovm/tests/files/std11.gno new file mode 100644 index 00000000000..ce02fa11ec3 --- /dev/null +++ b/gnovm/tests/files/std11.gno @@ -0,0 +1,14 @@ +package main + +import "std" + +func main() { + defer func() { + // assert panic is recoverable + println(recover()) + }() + std.GetCallerAt(42) +} + +// Output: +// frame not found diff --git a/gnovm/tests/files/std9.gno b/gnovm/tests/files/std9.gno new file mode 100644 index 00000000000..95ccfb2c8a4 --- /dev/null +++ b/gnovm/tests/files/std9.gno @@ -0,0 +1,14 @@ +package main + +import "std" + +func main() { + defer func() { + // assert panic is recoverable + println(recover()) + }() + std.AssertOriginCall() +} + +// Output: +// invalid non-origin call diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index 67ca1ba51e2..63ccd088f98 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -485,6 +485,7 @@ func testPackageInjector(store gno.Store, pn *gno.PackageNode) { func(m *gno.Machine) { if !isOriginCall(m) { m.Panic(typedString("invalid non-origin call")) + return } }, ) @@ -515,13 +516,15 @@ func testPackageInjector(store gno.Store, pn *gno.PackageNode) { arg0 := m.LastBlock().GetParams1().TV n := arg0.GetInt() if n <= 0 { - panic("GetCallerAt requires positive arg") + m.Panic(typedString("GetCallerAt requires positive arg")) + return } if n > m.NumFrames()-1 { // NOTE: the last frame's LastPackage // is set to the original non-frame // package, so need this check. - panic("frame not found") + m.Panic(typedString("frame not found")) + return } var pkgAddr string if n == m.NumFrames()-1 {