diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index 77631a0190f..53973b15fbf 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -11,7 +11,7 @@ require ( github.com/charmbracelet/log v0.3.1 github.com/fsnotify/fsnotify v1.7.0 github.com/gnolang/gno v0.0.0-00010101000000-000000000000 - github.com/gorilla/websocket v1.5.1 + github.com/gorilla/websocket v1.5.3 github.com/muesli/termenv v0.15.2 github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 @@ -21,7 +21,7 @@ require ( require ( dario.cat/mergo v1.0.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect @@ -43,7 +43,7 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.18 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/ff/v3 v3.4.0 // indirect @@ -56,7 +56,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect - go.etcd.io/bbolt v1.3.9 // indirect + go.etcd.io/bbolt v1.3.10 // indirect go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect @@ -73,7 +73,7 @@ require ( golang.org/x/net v0.27.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.23.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum index f4eb9423c21..2b5c964a014 100644 --- a/contribs/gnodev/go.sum +++ b/contribs/gnodev/go.sum @@ -9,8 +9,8 @@ github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7 github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= -github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= @@ -93,8 +93,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= @@ -117,8 +117,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= @@ -164,8 +164,8 @@ github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= @@ -229,8 +229,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/contribs/gnokeykc/go.mod b/contribs/gnokeykc/go.mod index d86c71558fb..9531b94a628 100644 --- a/contribs/gnokeykc/go.mod +++ b/contribs/gnokeykc/go.mod @@ -13,7 +13,7 @@ require ( require ( github.com/alessio/shellescape v1.4.1 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect @@ -28,7 +28,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect diff --git a/contribs/gnokeykc/go.sum b/contribs/gnokeykc/go.sum index e20600ea557..20c847e59f8 100644 --- a/contribs/gnokeykc/go.sum +++ b/contribs/gnokeykc/go.sum @@ -9,8 +9,8 @@ github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7 github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= -github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= @@ -82,8 +82,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -138,8 +138,8 @@ github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= diff --git a/examples/gno.land/r/demo/profile/render.gno b/examples/gno.land/r/demo/profile/render.gno index 4ff295e65eb..79d1078a997 100644 --- a/examples/gno.land/r/demo/profile/render.gno +++ b/examples/gno.land/r/demo/profile/render.gno @@ -2,6 +2,7 @@ package profile import ( "bytes" + "net/url" "std" "gno.land/p/demo/mux" @@ -83,7 +84,7 @@ func fieldHandler(res *mux.ResponseWriter, req *mux.Request) { if _, ok := stringFields[field]; ok { value = ufmt.Sprintf("%s", GetStringField(address, field, "n/a")) - editLink = ufmt.Sprintf(SetStringFieldURL+"&addr=%s&value=%s", field, addr, value) + editLink = ufmt.Sprintf(SetStringFieldURL+"&addr=%s&value=%s", field, addr, url.QueryEscape(value)) } else if _, ok := intFields[field]; ok { value = ufmt.Sprintf("%d", GetIntField(address, field, 0)) editLink = ufmt.Sprintf(SetIntFieldURL+"&addr=%s&value=%s", field, addr, value) diff --git a/gno.land/cmd/gnoland/testdata/panic.txtar b/gno.land/cmd/gnoland/testdata/panic.txtar new file mode 100644 index 00000000000..2b964d80751 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/panic.txtar @@ -0,0 +1,27 @@ +# test panic + +loadpkg gno.land/r/demo/panic $WORK + +# start a new node +gnoland start + + +! gnokey maketx call -pkgpath gno.land/r/demo/panic --func Trigger --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast -chainid=tendermint_test test1 + +stderr 'p\\(\)' +stderr 'gno.land/r/demo/panic/panic.gno:5' +stderr 'pkg\\.Trigger\(\)' +stderr 'gno.land/r/demo/panic/panic.gno:9' + +-- panic.gno -- +package main + +func p() { + i := "here" + panic(i) +} + +func Trigger() { + p() +} + diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 934c4557dd0..615be2029fe 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -537,12 +537,15 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { m.SetActivePackage(mpv) defer func() { if r := recover(); r != nil { - switch r.(type) { + switch r := r.(type) { case store.OutOfGasException: // panic in consumeGas() panic(r) + case gno.UnhandledPanicError: + err = errors.Wrap(fmt.Errorf("%v", r.Error()), "VM call panic: %s\nStacktrace: %s\n", + r.Error(), m.ExceptionsStacktrace()) default: - err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\n%s\n", - r, m.String()) + err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\nMachine State:%s\nStacktrace: %s\n", + r, m.String(), m.Stacktrace().String()) return } } diff --git a/gnovm/cmd/gno/fmt.go b/gnovm/cmd/gno/fmt.go index 7c5ad42c2b0..de6c28c4df0 100644 --- a/gnovm/cmd/gno/fmt.go +++ b/gnovm/cmd/gno/fmt.go @@ -162,7 +162,7 @@ func fmtProcessSingleFile(cfg *fmtCfg, file string, processFile fmtProcessFileFu } if !cfg.write { if !cfg.diff && !cfg.quiet { - io.Println(string(out)) + io.Out().Write(out) } return true } diff --git a/gnovm/cmd/gno/run.go b/gnovm/cmd/gno/run.go index 39306d59cb0..cfbfe995a46 100644 --- a/gnovm/cmd/gno/run.go +++ b/gnovm/cmd/gno/run.go @@ -188,8 +188,14 @@ func listNonTestFiles(dir string) ([]string, error) { func runExpr(m *gno.Machine, expr string) { defer func() { if r := recover(); r != nil { - fmt.Printf("panic running expression %s: %v\n%s\n", - expr, r, m.String()) + switch r := r.(type) { + case gno.UnhandledPanicError: + fmt.Printf("panic running expression %s: %v\nStacktrace: %s\n", + expr, r.Error(), m.ExceptionsStacktrace()) + default: + fmt.Printf("panic running expression %s: %v\nMachine State:%s\nStacktrace: %s\n", + expr, r, m.String(), m.Stacktrace().String()) + } panic(r) } }() diff --git a/gnovm/cmd/gno/testdata/gno_fmt/empty.txtar b/gnovm/cmd/gno/testdata/gno_fmt/empty.txtar index 14f85227335..97d367f6fdb 100644 --- a/gnovm/cmd/gno/testdata/gno_fmt/empty.txtar +++ b/gnovm/cmd/gno/testdata/gno_fmt/empty.txtar @@ -6,5 +6,4 @@ cmp stderr stderr.golden package hello -- stdout.golden.gno -- package hello - -- stderr.golden -- diff --git a/gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar b/gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar index bc186f98b6f..3d50f4e6428 100644 --- a/gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar +++ b/gnovm/cmd/gno/testdata/gno_fmt/import_cleaning.txtar @@ -26,5 +26,4 @@ var rand = &S{} package testdata var yes = rand.Val - -- stderr.golden -- diff --git a/gnovm/cmd/gno/testdata/gno_fmt/include.txtar b/gnovm/cmd/gno/testdata/gno_fmt/include.txtar index 92767983705..1cfedd06ad4 100644 --- a/gnovm/cmd/gno/testdata/gno_fmt/include.txtar +++ b/gnovm/cmd/gno/testdata/gno_fmt/include.txtar @@ -27,5 +27,4 @@ package testdata import "gno.land/r/test/mypkg" var myVar = mypkg.HelloFromMyPkg() - -- stderr.golden -- diff --git a/gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar b/gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar index 9e16f33591d..4002902bc59 100644 --- a/gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar +++ b/gnovm/cmd/gno/testdata/gno_fmt/multi_import.txtar @@ -92,5 +92,4 @@ func myBlog() *blog.Blog { return &blog.Blog{} } - -- stderr.golden -- diff --git a/gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar b/gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar index 56fca6ddbd7..6470fe76297 100644 --- a/gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar +++ b/gnovm/cmd/gno/testdata/gno_fmt/noimport_format.txtar @@ -35,5 +35,4 @@ import ( ) var yes = rand.Val - -- stderr.golden -- diff --git a/gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar b/gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar index 50d0970497e..19ad27b2f1a 100644 --- a/gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar +++ b/gnovm/cmd/gno/testdata/gno_fmt/shadow_import.txtar @@ -49,5 +49,4 @@ import ( func main() { println("a", v1.Get("a")) } - -- stderr.golden -- diff --git a/gnovm/pkg/gnolang/debugger_test.go b/gnovm/pkg/gnolang/debugger_test.go index 10d7c5ce250..16ecb91fb3c 100644 --- a/gnovm/pkg/gnolang/debugger_test.go +++ b/gnovm/pkg/gnolang/debugger_test.go @@ -24,7 +24,7 @@ type writeNopCloser struct{ io.Writer } func (writeNopCloser) Close() error { return nil } // TODO (Marc): move evalTest to gnovm/tests package and remove code duplicates -func evalTest(debugAddr, in, file string) (out, err string) { +func evalTest(debugAddr, in, file string) (out, err, stacktrace string) { bout := bytes.NewBufferString("") berr := bytes.NewBufferString("") stdin := bytes.NewBufferString(in) @@ -58,6 +58,18 @@ func evalTest(debugAddr, in, file string) (out, err string) { }) defer m.Release() + defer func() { + if r := recover(); r != nil { + switch r.(type) { + case gnolang.UnhandledPanicError: + stacktrace = m.ExceptionsStacktrace() + default: + stacktrace = m.Stacktrace().String() + } + stacktrace = strings.TrimSpace(strings.ReplaceAll(stacktrace, "../../tests/files/", "files/")) + panic(r) + } + }() if debugAddr != "" { if e := m.Debugger.Serve(debugAddr); e != nil { @@ -69,7 +81,7 @@ func evalTest(debugAddr, in, file string) (out, err string) { m.RunFiles(f) ex, _ := gnolang.ParseExpr("main()") m.Eval(ex) - out, err = bout.String(), berr.String() + out, err, stacktrace = bout.String(), berr.String(), m.ExceptionsStacktrace() return } @@ -78,7 +90,7 @@ func runDebugTest(t *testing.T, targetPath string, tests []dtest) { for _, test := range tests { t.Run("", func(t *testing.T) { - out, err := evalTest("", test.in, targetPath) + out, err, _ := evalTest("", test.in, targetPath) t.Log("in:", test.in, "out:", out, "err:", err) if !strings.Contains(out, test.out) { t.Errorf("unexpected output\nwant\"%s\"\n got \"%s\"", test.out, out) @@ -194,7 +206,7 @@ func TestRemoteDebug(t *testing.T) { } func TestRemoteError(t *testing.T) { - _, err := evalTest(":xxx", "", debugTarget) + _, err, _ := evalTest(":xxx", "", debugTarget) t.Log("err:", err) if !strings.Contains(err, "tcp/xxx: unknown port") && !strings.Contains(err, "tcp/xxx: nodename nor servname provided, or not known") { diff --git a/gnovm/pkg/gnolang/eval_test.go b/gnovm/pkg/gnolang/eval_test.go index fdd8e0204d1..5aa5bcca462 100644 --- a/gnovm/pkg/gnolang/eval_test.go +++ b/gnovm/pkg/gnolang/eval_test.go @@ -3,6 +3,7 @@ package gnolang_test import ( "os" "path" + "sort" "strings" "testing" ) @@ -14,17 +15,21 @@ func TestEvalFiles(t *testing.T) { t.Fatal(err) } for _, f := range files { - wantOut, wantErr, ok := testData(dir, f) + wantOut, wantErr, wantStacktrace, ok := testData(dir, f) if !ok { continue } t.Run(f.Name(), func(t *testing.T) { - out, err := evalTest("", "", path.Join(dir, f.Name())) + out, err, stacktrace := evalTest("", "", path.Join(dir, f.Name())) if wantErr != "" && !strings.Contains(err, wantErr) || wantErr == "" && err != "" { t.Fatalf("unexpected error\nWant: %s\n Got: %s", wantErr, err) } + + if wantStacktrace != "" && !strings.Contains(stacktrace, wantStacktrace) { + t.Fatalf("unexpected stacktrace\nWant: %s\n Got: %s", wantStacktrace, stacktrace) + } if wantOut != "" && out != wantOut { t.Fatalf("unexpected output\nWant: %s\n Got: %s", wantOut, out) } @@ -33,30 +38,63 @@ func TestEvalFiles(t *testing.T) { } // testData returns the expected output and error string, and true if entry is valid. -func testData(dir string, f os.DirEntry) (testOut, testErr string, ok bool) { +func testData(dir string, f os.DirEntry) (testOut, testErr, testStacktrace string, ok bool) { if f.IsDir() { - return "", "", false + return } name := path.Join(dir, f.Name()) if !strings.HasSuffix(name, ".gno") || strings.HasSuffix(name, "_long.gno") { - return "", "", false + return } buf, err := os.ReadFile(name) if err != nil { - return "", "", false + return } str := string(buf) if strings.Contains(str, "// PKGPATH:") { - return "", "", false + return } - return commentFrom(str, "\n// Output:"), commentFrom(str, "\n// Error:"), true + + res := commentFrom(str, []string{"\n// Output:", "\n// Error:", "\n// Stacktrace:"}) + + return res[0], res[1], res[2], true } -// commentFrom returns the content from a trailing comment block in s starting with delim. -func commentFrom(s, delim string) string { - index := strings.Index(s, delim) - if index < 0 { - return "" +type directive struct { + delim string + res string + index int +} + +// commentFrom returns the comments from s that are between the delimiters. +func commentFrom(s string, delims []string) []string { + directives := make([]directive, len(delims)) + directivesFound := make([]*directive, 0, len(delims)) + + for i, delim := range delims { + index := strings.Index(s, delim) + directives[i] = directive{delim: delim, index: index} + if index >= 0 { + directivesFound = append(directivesFound, &directives[i]) + } } - return strings.TrimSpace(strings.ReplaceAll(s[index+len(delim):], "\n// ", "\n")) + sort.Slice(directivesFound, func(i, j int) bool { + return directivesFound[i].index < directivesFound[j].index + }) + + for i := range directivesFound { + next := len(s) + if i != len(directivesFound)-1 { + next = directivesFound[i+1].index + } + + directivesFound[i].res = strings.TrimSpace(strings.ReplaceAll(s[directivesFound[i].index+len(directivesFound[i].delim):next], "\n// ", "\n")) + } + + res := make([]string, len(directives)) + for i, d := range directives { + res[i] = d.res + } + + return res } diff --git a/gnovm/pkg/gnolang/frame.go b/gnovm/pkg/gnolang/frame.go index c808fc111b0..2ac1027eb32 100644 --- a/gnovm/pkg/gnolang/frame.go +++ b/gnovm/pkg/gnolang/frame.go @@ -2,8 +2,11 @@ package gnolang import ( "fmt" + "strings" ) +const maxStacktraceSize = 128 + //---------------------------------------- // (runtime) Frame @@ -64,6 +67,10 @@ func (fr Frame) String() string { } } +func (fr *Frame) IsCall() bool { + return fr.Func != nil || fr.GoFunc != nil +} + func (fr *Frame) PushDefer(dfr Defer) { fr.Defers = append(fr.Defers, dfr) } @@ -92,3 +99,119 @@ type Defer struct { // a panic occurs and is decremented each time a panic is recovered. PanicScope uint } + +type StacktraceCall struct { + Stmt Stmt + Frame *Frame +} +type Stacktrace struct { + Calls []StacktraceCall + NumFramesElided int +} + +func (s Stacktrace) String() string { + var builder strings.Builder + + for i := 0; i < len(s.Calls); i++ { + if s.NumFramesElided > 0 && i == maxStacktraceSize/2 { + fmt.Fprintf(&builder, "...%d frame(s) elided...\n", s.NumFramesElided) + } + + call := s.Calls[i] + cx := call.Frame.Source.(*CallExpr) + switch { + case call.Frame.Func != nil && call.Frame.Func.IsNative(): + fmt.Fprintf(&builder, "%s\n", toExprTrace(cx)) + fmt.Fprintf(&builder, " gonative:%s.%s\n", call.Frame.Func.NativePkg, call.Frame.Func.NativeName) + case call.Frame.Func != nil: + fmt.Fprintf(&builder, "%s\n", toExprTrace(cx)) + fmt.Fprintf(&builder, " %s/%s:%d\n", call.Frame.Func.PkgPath, call.Frame.Func.FileName, call.Stmt.GetLine()) + case call.Frame.GoFunc != nil: + fmt.Fprintf(&builder, "%s\n", toExprTrace(cx)) + fmt.Fprintf(&builder, " gofunction:%s\n", call.Frame.GoFunc.Value.Type()) + default: + panic("StacktraceCall has a non-call Frame") + } + } + return builder.String() +} + +func toExprTrace(ex Expr) string { + switch ex := ex.(type) { + case *CallExpr: + s := make([]string, len(ex.Args)) + for i, arg := range ex.Args { + s[i] = toExprTrace(arg) + } + return fmt.Sprintf("%s(%s)", toExprTrace(ex.Func), strings.Join(s, ",")) + case *BinaryExpr: + return fmt.Sprintf("%s %s %s", toExprTrace(ex.Left), ex.Op.TokenString(), toExprTrace(ex.Right)) + case *UnaryExpr: + return fmt.Sprintf("%s%s", ex.Op.TokenString(), toExprTrace(ex.X)) + case *SelectorExpr: + return fmt.Sprintf("%s.%s", toExprTrace(ex.X), ex.Sel) + case *IndexExpr: + return fmt.Sprintf("%s[%s]", toExprTrace(ex.X), toExprTrace(ex.Index)) + case *StarExpr: + return fmt.Sprintf("*%s", toExprTrace(ex.X)) + case *RefExpr: + return fmt.Sprintf("&%s", toExprTrace(ex.X)) + case *CompositeLitExpr: + lenEl := len(ex.Elts) + if ex.Type == nil { + return fmt.Sprintf("", lenEl) + } + + return fmt.Sprintf("%s", toExprTrace(ex.Type), lenEl) + case *FuncLitExpr: + return fmt.Sprintf("%s{ ... }", toExprTrace(&ex.Type)) + case *TypeAssertExpr: + return fmt.Sprintf("%s.(%s)", toExprTrace(ex.X), toExprTrace(ex.Type)) + case *ConstExpr: + return toConstExpTrace(ex) + case *NameExpr, *BasicLitExpr, *SliceExpr: + return ex.String() + } + + return ex.String() +} + +func toConstExpTrace(cte *ConstExpr) string { + tv := cte.TypedValue + + switch bt := baseOf(tv.T).(type) { + case PrimitiveType: + switch bt { + case UntypedBoolType, BoolType: + return fmt.Sprintf("%t", tv.GetBool()) + case UntypedStringType, StringType: + return tv.GetString() + case IntType: + return fmt.Sprintf("%d", tv.GetInt()) + case Int8Type: + return fmt.Sprintf("%d", tv.GetInt8()) + case Int16Type: + return fmt.Sprintf("%d", tv.GetInt16()) + case UntypedRuneType, Int32Type: + return fmt.Sprintf("%d", tv.GetInt32()) + case Int64Type: + return fmt.Sprintf("%d", tv.GetInt64()) + case UintType: + return fmt.Sprintf("%d", tv.GetUint()) + case Uint8Type: + return fmt.Sprintf("%d", tv.GetUint8()) + case Uint16Type: + return fmt.Sprintf("%d", tv.GetUint16()) + case Uint32Type: + return fmt.Sprintf("%d", tv.GetUint32()) + case Uint64Type: + return fmt.Sprintf("%d", tv.GetUint64()) + case Float32Type: + return fmt.Sprintf("%v", tv.GetFloat32()) + case Float64Type: + return fmt.Sprintf("%v", tv.GetFloat64()) + } + } + + return tv.T.String() +} diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 850da3d3c0f..24f94abc10b 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -25,12 +25,23 @@ type Exception struct { // Frame is used to reference the frame a panic occurred in so that recover() knows if the // currently executing deferred function is able to recover from the panic. Frame *Frame + + Stacktrace Stacktrace } func (e Exception) Sprint(m *Machine) string { return e.Value.Sprint(m) } +// UnhandledPanicError represents an error thrown when a panic is not handled in the realm. +type UnhandledPanicError struct { + Descriptor string // Description of the unhandled panic. +} + +func (e UnhandledPanicError) Error() string { + return e.Descriptor +} + //---------------------------------------- // Machine @@ -457,6 +468,43 @@ func (m *Machine) TestFunc(t *testing.T, tv TypedValue) { }) } +// Stacktrace returns the stack trace of the machine. +// It collects the executions and frames from the machine's frames and statements. +func (m *Machine) Stacktrace() (stacktrace Stacktrace) { + if len(m.Frames) == 0 { + return + } + + calls := make([]StacktraceCall, 0, len(m.Stmts)) + nextStmtIndex := len(m.Stmts) - 1 + for i := len(m.Frames) - 1; i >= 0; i-- { + if m.Frames[i].IsCall() { + stm := m.Stmts[nextStmtIndex] + bs := stm.(*bodyStmt) + stm = bs.Body[bs.NextBodyIndex-1] + calls = append(calls, StacktraceCall{ + Stmt: stm, + Frame: m.Frames[i], + }) + } + // if the frame is a call, the next statement is the last statement of the frame. + nextStmtIndex = m.Frames[i].NumStmts - 1 + } + + // if the stacktrace is too long, we trim it down to maxStacktraceSize + if len(calls) > maxStacktraceSize { + const halfMax = maxStacktraceSize / 2 + + stacktrace.NumFramesElided = len(calls) - maxStacktraceSize + calls = append(calls[:halfMax], calls[len(calls)-halfMax:]...) + calls = calls[:len(calls):len(calls)] // makes remaining part of "calls" GC'able + } + + stacktrace.Calls = calls + + return +} + // in case of panic, inject location information to exception. func (m *Machine) injectLocOnPanic() { if r := recover(); r != nil { @@ -736,8 +784,14 @@ func (m *Machine) resavePackageValues(rlm *Realm) { func (m *Machine) RunFunc(fn Name) { defer func() { if r := recover(); r != nil { - fmt.Printf("Machine.RunFunc(%q) panic: %v\n%s\n", - fn, r, m.String()) + switch r := r.(type) { + case UnhandledPanicError: + fmt.Printf("Machine.RunFunc(%q) panic: %s\nStacktrace: %s\n", + fn, r.Error(), m.ExceptionsStacktrace()) + default: + fmt.Printf("Machine.RunFunc(%q) panic: %v\nMachine State:%s\nStacktrace: %s\n", + fn, r, m.String(), m.Stacktrace().String()) + } panic(r) } }() @@ -747,8 +801,14 @@ func (m *Machine) RunFunc(fn Name) { func (m *Machine) RunMain() { defer func() { if r := recover(); r != nil { - fmt.Printf("Machine.RunMain() panic: %v\n%s\n", - r, m.String()) + switch r := r.(type) { + case UnhandledPanicError: + fmt.Printf("Machine.RunMain() panic: %s\nStacktrace: %s\n", + r.Error(), m.ExceptionsStacktrace()) + default: + fmt.Printf("Machine.RunMain() panic: %v\nMachine State:%s\nStacktrace: %s\n", + r, m.String(), m.Stacktrace()) + } panic(r) } }() @@ -1606,11 +1666,11 @@ func (m *Machine) PopStmt() Stmt { } if bs, ok := s.(*bodyStmt); ok { return bs.PopActiveStmt() - } else { - // general case. - m.Stmts = m.Stmts[:numStmts-1] - return s } + + m.Stmts = m.Stmts[:numStmts-1] + + return s } func (m *Machine) ForcePopStmt() (s Stmt) { @@ -1855,6 +1915,7 @@ func (m *Machine) PopFrame() Frame { m.Printf("-F %#v\n", f) } m.Frames = m.Frames[:numFrames-1] + return *f } @@ -1874,8 +1935,7 @@ func (m *Machine) PopFrameAndReturn() { fr := m.PopFrame() fr.Popped = true if debug { - // TODO: optimize with fr.IsCall - if fr.Func == nil && fr.GoFunc == nil { + if !fr.IsCall() { panic("unexpected non-call (loop) frame") } } @@ -1951,8 +2011,7 @@ func (m *Machine) lastCallFrame(n int, mustBeFound bool) *Frame { } for i := len(m.Frames) - 1; i >= 0; i-- { fr := m.Frames[i] - if fr.Func != nil || fr.GoFunc != nil { - // TODO: optimize with fr.IsCall + if fr.IsCall() { if n == 1 { return fr } else { @@ -1973,8 +2032,7 @@ func (m *Machine) lastCallFrame(n int, mustBeFound bool) *Frame { func (m *Machine) PopUntilLastCallFrame() *Frame { for i := len(m.Frames) - 1; i >= 0; i-- { fr := m.Frames[i] - if fr.Func != nil || fr.GoFunc != nil { - // TODO: optimize with fr.IsCall + if fr.IsCall() { m.Frames = m.Frames[:i+1] return fr } @@ -2085,8 +2143,9 @@ func (m *Machine) Panic(ex TypedValue) { m.Exceptions = append( m.Exceptions, Exception{ - Value: ex, - Frame: m.MustLastCallFrame(1), + Value: ex, + Frame: m.MustLastCallFrame(1), + Stacktrace: m.Stacktrace(), }, ) @@ -2231,6 +2290,30 @@ func (m *Machine) String() string { return builder.String() } +func (m *Machine) ExceptionsStacktrace() string { + if len(m.Exceptions) == 0 { + return "" + } + + var builder strings.Builder + + ex := m.Exceptions[0] + builder.WriteString("panic: " + ex.Sprint(m) + "\n") + builder.WriteString(ex.Stacktrace.String()) + + switch { + case len(m.Exceptions) > 2: + fmt.Fprintf(&builder, "... %d panic(s) elided ...\n", len(m.Exceptions)-2) + fallthrough // to print last exception + case len(m.Exceptions) == 2: + ex = m.Exceptions[len(m.Exceptions)-1] + builder.WriteString("panic: " + ex.Sprint(m) + "\n") + builder.WriteString(ex.Stacktrace.String()) + } + + return builder.String() +} + //---------------------------------------- // utility diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index c578d2a3ec3..b18ed157ca6 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -4,6 +4,7 @@ import ( "fmt" "go/parser" "go/token" + "math" "os" "path/filepath" "reflect" @@ -1802,7 +1803,7 @@ func (sb *StaticBlock) Define2(isConst bool, n Name, st Type, tv TypedValue) { if int(sb.NumNames) != len(sb.Names) { panic("StaticBlock.NumNames and len(.Names) mismatch") } - if (1<<16 - 1) < sb.NumNames { + if sb.NumNames == math.MaxUint16 { panic("too many variables in block") } if tv.T == nil && tv.V != nil { diff --git a/gnovm/pkg/gnolang/nodes_test.go b/gnovm/pkg/gnolang/nodes_test.go new file mode 100644 index 00000000000..2c3a03d8c09 --- /dev/null +++ b/gnovm/pkg/gnolang/nodes_test.go @@ -0,0 +1,44 @@ +package gnolang_test + +import ( + "math" + "testing" + + "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func TestStaticBlock_Define2_MaxNames(t *testing.T) { + defer func() { + if r := recover(); r != nil { + panicString, ok := r.(string) + if !ok { + t.Errorf("expected panic string, got %v", r) + } + + if panicString != "too many variables in block" { + t.Errorf("expected panic string to be 'too many variables in block', got '%s'", panicString) + } + + return + } + + // If it didn't panic, fail. + t.Errorf("expected panic when exceeding maximum number of names") + }() + + staticBlock := new(gnolang.StaticBlock) + staticBlock.NumNames = math.MaxUint16 - 1 + staticBlock.Names = make([]gnolang.Name, staticBlock.NumNames) + + // Adding one more is okay. + staticBlock.Define2(false, gnolang.Name("a"), gnolang.BoolType, gnolang.TypedValue{T: gnolang.BoolType}) + if staticBlock.NumNames != math.MaxUint16 { + t.Errorf("expected NumNames to be %d, got %d", math.MaxUint16, staticBlock.NumNames) + } + if len(staticBlock.Names) != math.MaxUint16 { + t.Errorf("expected len(Names) to be %d, got %d", math.MaxUint16, len(staticBlock.Names)) + } + + // This one should panic because the maximum number of names has been reached. + staticBlock.Define2(false, gnolang.Name("a"), gnolang.BoolType, gnolang.TypedValue{T: gnolang.BoolType}) +} diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 5479ee6d5ae..15531ec610d 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -427,7 +427,9 @@ func (m *Machine) doOpPanic2() { for i, ex := range m.Exceptions { exs[i] = ex.Sprint(m) } - panic(strings.Join(exs, "\n\t")) + panic(UnhandledPanicError{ + Descriptor: strings.Join(exs, "\n\t"), + }) } m.PushOp(OpPanic2) m.PushOp(OpReturnCallDefers) // XXX rename, not return? diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index d21e9bf0efd..5a710723b86 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -427,6 +427,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { switch n := n.(type) { // TRANS_ENTER ----------------------- case *AssignStmt: + checkValDefineMismatch(n) + if n.Op == DEFINE { for _, lx := range n.Lhs { ln := lx.(*NameExpr).Name @@ -445,7 +447,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } else { // nothing defined. } - // TRANS_ENTER ----------------------- case *ImportDecl, *ValueDecl, *TypeDecl, *FuncDecl: // NOTE func decl usually must happen with a @@ -457,8 +458,12 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // skip declarations already predefined // (e.g. through recursion for a dependent) } else { + d := n.(Decl) + if cd, ok := d.(*ValueDecl); ok { + checkValDefineMismatch(cd) + } // recursively predefine dependencies. - d2, ppd := predefineNow(store, last, n.(Decl)) + d2, ppd := predefineNow(store, last, d) if ppd { return d2, TRANS_SKIP } else { @@ -2162,8 +2167,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // special case if `var a, b, c T? = f()` form. cx := n.Values[0].(*CallExpr) tt := evalStaticTypeOfRaw(store, last, cx).(*tupleType) - if len(tt.Elts) != numNames { - panic("should not happen") + if rLen := len(tt.Elts); rLen != numNames { + panic(fmt.Sprintf("assignment mismatch: %d variable(s) but %s returns %d value(s)", numNames, cx.Func.String(), rLen)) } if n.Type != nil { // only a single type can be specified. @@ -2182,8 +2187,16 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } } } else if len(n.Values) != 0 && numNames != len(n.Values) { - panic("should not happen") + panic(fmt.Sprintf("assignment mismatch: %d variable(s) but %d value(s)", numNames, len(n.Values))) } else { // general case + for _, v := range n.Values { + if cx, ok := v.(*CallExpr); ok { + tt, ok := evalStaticTypeOfRaw(store, last, cx).(*tupleType) + if ok && len(tt.Elts) != 1 { + panic(fmt.Sprintf("multiple-value %s (value of type %s) in single-value context", cx.Func.String(), tt.Elts)) + } + } + } // evaluate types and convert consts. if n.Type != nil { // only a single type can be specified. diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 3f25667f353..31025fef152 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -215,6 +215,70 @@ func assertAssignableTo(xt, dt Type, autoNative bool) { } } +// checkValDefineMismatch checks for mismatch between the number of variables and values in a ValueDecl or AssignStmt. +func checkValDefineMismatch(n Node) { + var ( + valueDecl *ValueDecl + assign *AssignStmt + values []Expr + numNames int + numValues int + ) + + switch x := n.(type) { + case *ValueDecl: + valueDecl = x + numNames = len(valueDecl.NameExprs) + numValues = len(valueDecl.Values) + values = valueDecl.Values + case *AssignStmt: + if x.Op != DEFINE { + return + } + + assign = x + numNames = len(assign.Lhs) + numValues = len(assign.Rhs) + values = assign.Rhs + default: + panic(fmt.Sprintf("unexpected node type %T", n)) + } + + if numValues == 0 || numValues == numNames { + return + } + + // Special case for single value. + // If the value is a call expression, type assertion, or index expression, + // it can be assigned to multiple variables. + if numValues == 1 { + switch values[0].(type) { + case *CallExpr: + return + case *TypeAssertExpr: + if numNames != 2 { + panic(fmt.Sprintf("assignment mismatch: %d variable(s) but %d value(s)", numNames, numValues)) + } + return + case *IndexExpr: + if numNames != 2 { + panic(fmt.Sprintf("assignment mismatch: %d variable(s) but %d value(s)", numNames, numValues)) + } + return + } + } + + if valueDecl != nil { + if numNames > numValues { + panic(fmt.Sprintf("missing init expr for %s", valueDecl.NameExprs[numValues].String())) + } + + panic(fmt.Sprintf("extra init expr %s", values[numNames].String())) + } + + panic(fmt.Sprintf("assignment mismatch: %d variable(s) but %d value(s)", numNames, numValues)) +} + // Assert that xt can be assigned as dt (dest type). // If autoNative is true, a broad range of xt can match against // a target native dt type, if and only if dt is a native type. diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 8ab60145bd5..3fea714b142 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -109,7 +109,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { opt(&f) } - directives, pkgPath, resWanted, errWanted, rops, maxAlloc, send := wantedFromComment(path) + directives, pkgPath, resWanted, errWanted, rops, stacktraceWanted, maxAlloc, send := wantedFromComment(path) if pkgPath == "" { pkgPath = "main" } @@ -124,6 +124,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { store := TestStore(rootDir, "./files", stdin, stdout, stderr, mode) store.SetLogStoreOps(true) m := testMachineCustom(store, pkgPath, stdout, maxAlloc, send) + checkMachineIsEmpty := true // TODO support stdlib groups, but make testing safe; // e.g. not be able to make network connections. @@ -259,6 +260,8 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { errstr = v.Sprint(m) case *gno.PreprocessError: errstr = v.Unwrap().Error() + case gno.UnhandledPanicError: + errstr = v.Error() default: errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc)) } @@ -279,7 +282,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { // NOTE: ignores any gno.GetDebugErrors(). gno.ClearDebugErrors() - return nil // nothing more to do. + checkMachineIsEmpty = false // nothing more to do. } else { // record errors when errWanted is empty and pnc not nil if pnc != nil { @@ -307,7 +310,7 @@ 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 + checkMachineIsEmpty = false } case "Output": // panic if got unexpected error @@ -373,24 +376,51 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { } } } + case "Stacktrace": + if stacktraceWanted != "" { + var stacktrace string + + switch pnc.(type) { + case gno.UnhandledPanicError: + stacktrace = m.ExceptionsStacktrace() + default: + stacktrace = m.Stacktrace().String() + } + + if !strings.Contains(stacktrace, stacktraceWanted) { + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(stacktraceWanted), + B: difflib.SplitLines(stacktrace), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff)) + } + } + checkMachineIsEmpty = false default: - return nil + checkMachineIsEmpty = false } } } - // Check that machine is empty. - err = m.CheckEmpty() - if err != nil { - if f.logger != nil { - f.logger("last state: \n", m.String()) + if checkMachineIsEmpty { + // Check that machine is empty. + err = m.CheckEmpty() + if err != nil { + if f.logger != nil { + f.logger("last state: \n", m.String()) + } + panic(fmt.Sprintf("fail on %s: machine not empty after main: %v", path, err)) } - panic(fmt.Sprintf("fail on %s: machine not empty after main: %v", path, err)) } return nil } -func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops string, maxAlloc int64, send std.Coins) { +func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops, stacktrace string, maxAlloc int64, send std.Coins) { fset := token.NewFileSet() f, err2 := parser.ParseFile(fset, p, nil, parser.ParseComments) if err2 != nil { @@ -432,6 +462,10 @@ func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops s rops = strings.TrimPrefix(text, "Realm:\n") rops = strings.TrimSpace(rops) directives = append(directives, "Realm") + } else if strings.HasPrefix(text, "Stacktrace:\n") { + stacktrace = strings.TrimPrefix(text, "Stacktrace:\n") + stacktrace = strings.TrimSpace(stacktrace) + directives = append(directives, "Stacktrace") } else { // ignore unexpected. } diff --git a/gnovm/tests/files/assign24.gno b/gnovm/tests/files/assign24.gno new file mode 100644 index 00000000000..408258def92 --- /dev/null +++ b/gnovm/tests/files/assign24.gno @@ -0,0 +1,8 @@ +package main + +func main() { + a, b := 1 +} + +// Error: +// main/files/assign24.gno:4:2: assignment mismatch: 2 variable(s) but 1 value(s) diff --git a/gnovm/tests/files/assign25.gno b/gnovm/tests/files/assign25.gno new file mode 100644 index 00000000000..b945afc3b1f --- /dev/null +++ b/gnovm/tests/files/assign25.gno @@ -0,0 +1,14 @@ +package main + +func foo() (int, bool) { + return 1, true +} + +func main() { + a, b, c := 2, foo() + + println(a, b, c) +} + +// Error: +// main/files/assign25.gno:8:2: assignment mismatch: 3 variable(s) but 2 value(s) diff --git a/gnovm/tests/files/assign25b.gno b/gnovm/tests/files/assign25b.gno new file mode 100644 index 00000000000..886777324f5 --- /dev/null +++ b/gnovm/tests/files/assign25b.gno @@ -0,0 +1,14 @@ +package main + +func foo() (int, bool) { + return 1, true +} + +func main() { + a, b, c := 2, 3, 4, foo() + + println(a, b, c) +} + +// Error: +// main/files/assign25b.gno:8:2: assignment mismatch: 3 variable(s) but 4 value(s) diff --git a/gnovm/tests/files/assign26.gno b/gnovm/tests/files/assign26.gno new file mode 100644 index 00000000000..f974ae0b174 --- /dev/null +++ b/gnovm/tests/files/assign26.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var i interface{} = 1 + a, b, c := i.(int) +} + +// Error: +// main/files/assign26.gno:5:2: assignment mismatch: 3 variable(s) but 1 value(s) diff --git a/gnovm/tests/files/assign27.gno b/gnovm/tests/files/assign27.gno new file mode 100644 index 00000000000..14fc8bf4e37 --- /dev/null +++ b/gnovm/tests/files/assign27.gno @@ -0,0 +1,9 @@ +package main + +func main() { + s := []string{"1", "2"} + a, b, c := s[0] +} + +// Error: +// main/files/assign27.gno:5:2: assignment mismatch: 3 variable(s) but 1 value(s) diff --git a/gnovm/tests/files/assign28.gno b/gnovm/tests/files/assign28.gno new file mode 100644 index 00000000000..a0afe4f0e36 --- /dev/null +++ b/gnovm/tests/files/assign28.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + a, c := 1, 2, 3 + fmt.Println(a, c) +} + +// Error: +// main/files/assign28.gno:6:2: assignment mismatch: 2 variable(s) but 3 value(s) diff --git a/gnovm/tests/files/panic0.gno b/gnovm/tests/files/panic0.gno index 66a38b42c46..06460ca9d07 100644 --- a/gnovm/tests/files/panic0.gno +++ b/gnovm/tests/files/panic0.gno @@ -4,5 +4,10 @@ func main() { panic("wtf") } +// Stacktrace: +// panic: wtf +// main() +// main/files/panic0.gno:4 + // Error: // wtf diff --git a/gnovm/tests/files/panic0a.gno b/gnovm/tests/files/panic0a.gno new file mode 100644 index 00000000000..575bb7cce91 --- /dev/null +++ b/gnovm/tests/files/panic0a.gno @@ -0,0 +1,45 @@ +// Test panic with function call with all kind of expressions +package main + +type S struct { + s string +} + +func f(it1 int, it2, it3 int, pit *int, b bool, strs []string, s S, m map[string]string, t func(s string) string) { + panic("wtf") +} + +func main() { + vit := 1 + lit := []int{1} + var ( + pit *int = &vit + v interface{} + ) + b := true + v = 1 + + f( + v.(int), + lit[0], + *pit, + &vit, + !b, + []string{"a", "b"}, + S{s: "c"}, + map[string]string{"d": "gg", "test": "test"}, + func(s string) string { + return s + }, + ) +} + +// Stacktrace: +// panic: wtf +// f(v.((const-type int)),lit[0],*pit,&vit,!b,[](const-type string),S,map[(const-type string)] (const-type string),func(s (const-type string)) (const-type string){ ... }) +// main/files/panic0a.gno:9 +// main() +// main/files/panic0a.gno:22 + +// Error: +// wtf diff --git a/gnovm/tests/files/panic0b.gno b/gnovm/tests/files/panic0b.gno index bf3b343f785..55a7b21015a 100644 --- a/gnovm/tests/files/panic0b.gno +++ b/gnovm/tests/files/panic0b.gno @@ -14,6 +14,19 @@ func f() { panic("first") } +// Stacktrace: +// panic: first +// f() +// main/files/panic0b.gno:14 +// main() +// main/files/panic0b.gno:4 +// ... 1 panic(s) elided ... +// panic: third +// f() +// main/files/panic0b.gno:9 +// main() +// main/files/panic0b.gno:4 + // Error: // first // second diff --git a/gnovm/tests/files/panic0c.gno b/gnovm/tests/files/panic0c.gno new file mode 100644 index 00000000000..c331ee3bd17 --- /dev/null +++ b/gnovm/tests/files/panic0c.gno @@ -0,0 +1,87 @@ +package main + +type S struct { + s string +} + +func f( + s string, + b bool, + by byte, + it int, + it8 int8, + it16 int16, + it32 int32, + it64 int64, + uit uint, + uit8 uint8, + uit16 uint16, + uit32 uint32, + uit64 uint64, + ft32 float32, + ft64 float64, + strs []string, + st S, + m map[string]string, + t func(s string) string, +) { + panic("wtf") +} + +func main() { + strs := []string{"a", "b"} + st := S{"c"} + m := map[string]string{"d": "gg", "test": "test"} + t := func(s string) string { + return s + } + + const s string = "a" + + const b bool = true + + const by byte = 0 + + const it int = 1 + const it8 int8 = 1 + const it16 int16 = 1 + const it32 int32 = 1 + const it64 int64 = 1 + const uit uint = 1 + const uit8 uint8 = 1 + const uit16 uint16 = 1 + const uit32 uint32 = 1 + const uit64 uint64 = 1 + const ft32 float32 = 1 + const ft64 float64 = 1 + f( + s, + b, + by, + it, + it8, + it16, + it32, + it64, + uit, + uit8, + uit16, + uit32, + uit64, + ft32, + ft64, + strs, + st, + m, + t) +} + +// Stacktrace: +// panic: wtf +// f(a,true,0,1,1,1,1,1,1,1,1,1,1,1,1,strs,st,m,t) +// main/files/panic0c.gno:28 +// main() +// main/files/panic0c.gno:57 + +// Error: +// wtf diff --git a/gnovm/tests/files/panic1.gno b/gnovm/tests/files/panic1.gno index 483d43e53d1..235ba4a0b34 100644 --- a/gnovm/tests/files/panic1.gno +++ b/gnovm/tests/files/panic1.gno @@ -22,5 +22,10 @@ func main() { panic("here") } +// Stacktrace: +// panic: here +// main() +// main/files/panic1.gno:22 + // Error: // here diff --git a/gnovm/tests/files/panic2a.gno b/gnovm/tests/files/panic2a.gno new file mode 100644 index 00000000000..7310d6cce71 --- /dev/null +++ b/gnovm/tests/files/panic2a.gno @@ -0,0 +1,275 @@ +package main + +func p(i int) { + if i == 200 { + panic("here") + } + p(i + 1) +} + +func main() { + p(0) +} + +// Stacktrace: +// panic: here +// p(i + 1) +// main/files/panic2a.gno:5 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// ...74 frame(s) elided... +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(i + 1) +// main/files/panic2a.gno:7 +// p(0) +// main/files/panic2a.gno:7 +// main() +// main/files/panic2a.gno:11 + +// Error: +// here diff --git a/gnovm/tests/files/panic2b.gno b/gnovm/tests/files/panic2b.gno new file mode 100644 index 00000000000..7c356409aad --- /dev/null +++ b/gnovm/tests/files/panic2b.gno @@ -0,0 +1,44 @@ +package main + +func p(i int) { + defer func() { + panic("here") + }() + if i == 4 { + panic("here") + } + p(i + 1) +} + +func main() { + p(0) +} + +// Stacktrace: +// panic: here +// p(i + 1) +// main/files/panic2b.gno:8 +// p(i + 1) +// main/files/panic2b.gno:10 +// p(i + 1) +// main/files/panic2b.gno:10 +// p(i + 1) +// main/files/panic2b.gno:10 +// p(0) +// main/files/panic2b.gno:10 +// main() +// main/files/panic2b.gno:14 +// ... 4 panic(s) elided ... +// panic: here +// p(0) +// main/files/panic2b.gno:5 +// main() +// main/files/panic2b.gno:14 + +// Error: +// here +// here +// here +// here +// here +// here diff --git a/gnovm/tests/files/recover10.gno b/gnovm/tests/files/recover10.gno index 16dff4d4fed..de083a322a4 100644 --- a/gnovm/tests/files/recover10.gno +++ b/gnovm/tests/files/recover10.gno @@ -6,5 +6,10 @@ func main() { panic("ahhhhh") } +// Stacktrace: +// panic: ahhhhh +// main() +// main/files/recover10.gno:6 + // Error: // ahhhhh diff --git a/gnovm/tests/files/recover1b.gno b/gnovm/tests/files/recover1b.gno index 9e3b9ba72b6..978b4988329 100644 --- a/gnovm/tests/files/recover1b.gno +++ b/gnovm/tests/files/recover1b.gno @@ -10,5 +10,10 @@ func main() { panic("test panic") } +// Stacktrace: +// panic: other panic +// main() +// main/files/recover1b.gno:8 + // Error: // other panic diff --git a/gnovm/tests/files/recover8.gno b/gnovm/tests/files/recover8.gno index 53b31f05468..d144a4f986f 100644 --- a/gnovm/tests/files/recover8.gno +++ b/gnovm/tests/files/recover8.gno @@ -20,5 +20,12 @@ func main() { doSomething() } +// Stacktrace: +// panic: do something panic +// doSomething() +// main/files/recover8.gno:7 +// main() +// main/files/recover8.gno:20 + // Error: // do something panic diff --git a/gnovm/tests/files/std5_stdlibs.gno b/gnovm/tests/files/std5_stdlibs.gno index d8de58518f1..4afa09da8d3 100644 --- a/gnovm/tests/files/std5_stdlibs.gno +++ b/gnovm/tests/files/std5_stdlibs.gno @@ -11,5 +11,14 @@ func main() { println(caller2) } +// Stacktrace: +// panic: frame not found +// callerAt(n) +// gonative:std.callerAt +// std.GetCallerAt(2) +// std/native.gno:44 +// main() +// main/files/std5_stdlibs.gno:10 + // Error: // frame not found diff --git a/gnovm/tests/files/std8_stdlibs.gno b/gnovm/tests/files/std8_stdlibs.gno index 6964cec1d3d..ab5e15bd618 100644 --- a/gnovm/tests/files/std8_stdlibs.gno +++ b/gnovm/tests/files/std8_stdlibs.gno @@ -21,5 +21,18 @@ func main() { testutils.WrapCall(inner) } +// Stacktrace: +// panic: frame not found +// callerAt(n) +// gonative:std.callerAt +// std.GetCallerAt(4) +// std/native.gno:44 +// fn() +// main/files/std8_stdlibs.gno:16 +// testutils.WrapCall(inner) +// gno.land/p/demo/testutils/misc.gno:5 +// main() +// main/files/std8_stdlibs.gno:21 + // Error: // frame not found diff --git a/gnovm/tests/files/time17_native.gno b/gnovm/tests/files/time17_native.gno new file mode 100644 index 00000000000..6733c1381cb --- /dev/null +++ b/gnovm/tests/files/time17_native.gno @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + now := time.Now() + now.In(nil) +} + +// Error: +// time: missing Location in call to Time.In + +// Stacktrace: +// now.In(gonative{*time.Location}) +// gofunction:func(*time.Location) time.Time +// main() +// main/files/time17_native.gno:10 diff --git a/gnovm/tests/files/typeassert1.gno b/gnovm/tests/files/typeassert1.gno index 041034e4bd0..f6609a3d18c 100644 --- a/gnovm/tests/files/typeassert1.gno +++ b/gnovm/tests/files/typeassert1.gno @@ -9,5 +9,10 @@ func main() { _ = a.(A) } +// Stacktrace: +// panic: interface conversion: interface is nil, not main.A +// main() +// main/files/typeassert1.gno:9 + // Error: // interface conversion: interface is nil, not main.A diff --git a/gnovm/tests/files/typeassert2a.gno b/gnovm/tests/files/typeassert2a.gno index 0441bf83437..bfbd24d38bd 100644 --- a/gnovm/tests/files/typeassert2a.gno +++ b/gnovm/tests/files/typeassert2a.gno @@ -11,5 +11,10 @@ func main() { }() } +// Stacktrace: +// panic: interface conversion: interface is nil, not main.A +// main() +// main/files/typeassert2a.gno:10 + // Error: // interface conversion: interface is nil, not main.A diff --git a/gnovm/tests/files/typeassert9.gno b/gnovm/tests/files/typeassert9.gno index d9d5bad55af..6ea072661c1 100644 --- a/gnovm/tests/files/typeassert9.gno +++ b/gnovm/tests/files/typeassert9.gno @@ -16,5 +16,10 @@ func main() { _ = reader.(Writer) } +// Stacktrace: +// panic: interface conversion: interface is nil, not main.Writer +// main() +// main/files/typeassert9.gno:16 + // Error: // interface conversion: interface is nil, not main.Writer diff --git a/gnovm/tests/files/var18.gno b/gnovm/tests/files/var18.gno index 771ddcdffb5..f01d3871d39 100644 --- a/gnovm/tests/files/var18.gno +++ b/gnovm/tests/files/var18.gno @@ -1,8 +1,8 @@ package main func main() { - a, b, c := 1, 2 + var a, b, c = 1, 2 } // Error: -// main/files/var18.gno:4:2: assignment mismatch: 3 variables but 2 values +// main/files/var18.gno:4:6: missing init expr for c diff --git a/gnovm/tests/files/var19.gno b/gnovm/tests/files/var19.gno new file mode 100644 index 00000000000..cbdce802e0a --- /dev/null +++ b/gnovm/tests/files/var19.gno @@ -0,0 +1,11 @@ +package main + +func main() { + var a, b, c = 1, a+1 + println(a) + println(b) + println(c) +} + +// Error: +// main/files/var19.gno:4:6: missing init expr for c diff --git a/gnovm/tests/files/var20.gno b/gnovm/tests/files/var20.gno new file mode 100644 index 00000000000..e2455cbaed8 --- /dev/null +++ b/gnovm/tests/files/var20.gno @@ -0,0 +1,12 @@ +package main + +func r() int { + return 1 +} + +func main() { + var a, b, c = r() +} + +// Error: +// main/files/var20.gno:8:6: assignment mismatch: 3 variable(s) but r returns 1 value(s) diff --git a/gnovm/tests/files/var21.gno b/gnovm/tests/files/var21.gno new file mode 100644 index 00000000000..b593984aa87 --- /dev/null +++ b/gnovm/tests/files/var21.gno @@ -0,0 +1,14 @@ +package main + +func foo() (int, bool) { + return 1, true +} + +func main() { + var a, b = 2, foo() + + println(a, b) +} + +// Error: +// main/files/var21.gno:8:6: multiple-value foo (value of type [int bool]) in single-value context diff --git a/gnovm/tests/files/var22.gno b/gnovm/tests/files/var22.gno new file mode 100644 index 00000000000..3f85f0f156d --- /dev/null +++ b/gnovm/tests/files/var22.gno @@ -0,0 +1,14 @@ +package main + +func foo() (int, bool) { + return 1, true +} + +func main() { + var a, b, c = 2, foo() + + println(a, b, c) +} + +// Error: +// main/files/var22.gno:8:6: missing init expr for c diff --git a/gnovm/tests/files/var22b.gno b/gnovm/tests/files/var22b.gno new file mode 100644 index 00000000000..22a52fcc811 --- /dev/null +++ b/gnovm/tests/files/var22b.gno @@ -0,0 +1,14 @@ +package main + +func foo() (int, bool) { + return 1, true +} + +func main() { + var a, b, c = 2, 3, 4, foo() + + println(a, b, c) +} + +// Error: +// main/files/var22b.gno:8:6: extra init expr foo() diff --git a/gnovm/tests/files/var23.gno b/gnovm/tests/files/var23.gno new file mode 100644 index 00000000000..b9f98311411 --- /dev/null +++ b/gnovm/tests/files/var23.gno @@ -0,0 +1,9 @@ +package main + +var a, b, c = 1, 2 + +func main() { +} + +// Error: +// main/files/var23.gno:3:5: assignment mismatch: 3 variable(s) but 2 value(s) diff --git a/gnovm/tests/files/var24.gno b/gnovm/tests/files/var24.gno new file mode 100644 index 00000000000..ddfa82a1e4e --- /dev/null +++ b/gnovm/tests/files/var24.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var i interface{} = 1 + var a, b, c = i.(int) +} + +// Error: +// main/files/var24.gno:5:6: assignment mismatch: 3 variable(s) but 1 value(s) diff --git a/gnovm/tests/files/var25.gno b/gnovm/tests/files/var25.gno new file mode 100644 index 00000000000..999979dcf71 --- /dev/null +++ b/gnovm/tests/files/var25.gno @@ -0,0 +1,9 @@ +package main + +func main() { + s := []string{"1", "2"} + var a, b, c = s[0] +} + +// Error: +// main/files/var25.gno:5:6: assignment mismatch: 3 variable(s) but 1 value(s) diff --git a/gnovm/tests/files/var26.gno b/gnovm/tests/files/var26.gno new file mode 100644 index 00000000000..71b6f3a4eb0 --- /dev/null +++ b/gnovm/tests/files/var26.gno @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + var a, c = 1, 2, 3 + fmt.Println(a, c) +} + +// Error: +// main/files/var26.gno:6:6: extra init expr 3 diff --git a/gnovm/tests/files/zrealm_panic.gno b/gnovm/tests/files/zrealm_panic.gno new file mode 100644 index 00000000000..3864e2a7f7f --- /dev/null +++ b/gnovm/tests/files/zrealm_panic.gno @@ -0,0 +1,20 @@ +// PKGPATH: gno.land/r/test +package test + +type MyStruct struct{} + +func (ms MyStruct) Panic() { + panic("panic") +} + +func main() { + ms := MyStruct{} + ms.Panic() +} + +// Stacktrace: +// panic: panic +// ms.Panic() +// gno.land/r/test/main.gno:7 +// main() +// gno.land/r/test/main.gno:12 diff --git a/go.mod b/go.mod index c155a23f16b..d0845d73641 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.4 require ( dario.cat/mergo v1.0.0 - github.com/btcsuite/btcd/btcec/v2 v2.3.3 + github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/cockroachdb/apd/v3 v3.2.1 github.com/cosmos/ledger-cosmos-go v0.13.3 @@ -18,10 +18,10 @@ require ( github.com/golang/protobuf v1.5.4 github.com/google/gofuzz v1.2.0 github.com/gorilla/mux v1.8.1 - github.com/gorilla/websocket v1.5.1 + github.com/gorilla/websocket v1.5.3 github.com/gotuna/gotuna v0.6.0 github.com/libp2p/go-buffer-pool v0.1.0 - github.com/mattn/go-runewidth v0.0.15 + github.com/mattn/go-runewidth v0.0.16 github.com/pelletier/go-toml v1.9.5 github.com/peterbourgon/ff/v3 v3.4.0 github.com/pmezard/go-difflib v1.0.0 @@ -30,7 +30,7 @@ require ( github.com/rs/xid v1.5.0 github.com/stretchr/testify v1.9.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - go.etcd.io/bbolt v1.3.9 + go.etcd.io/bbolt v1.3.10 go.opentelemetry.io/otel v1.28.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 @@ -46,7 +46,7 @@ require ( golang.org/x/net v0.27.0 golang.org/x/sync v0.7.0 golang.org/x/term v0.22.0 - golang.org/x/tools v0.22.0 + golang.org/x/tools v0.23.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 9fde89e8cec..5cb6be26da2 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7 github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= -github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= @@ -89,8 +89,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= @@ -110,8 +110,9 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= @@ -153,8 +154,8 @@ github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= @@ -244,8 +245,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= diff --git a/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go b/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go index ccf2104a8d3..3e89515294d 100644 --- a/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go +++ b/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go @@ -15,10 +15,7 @@ import ( func (privKey PrivKeySecp256k1) Sign(msg []byte) ([]byte, error) { priv, _ := btcec.PrivKeyFromBytes(privKey[:]) - sig, err := ecdsa.SignCompact(priv, crypto.Sha256(msg), false) // ref uncompressed pubkey - if err != nil { - return nil, err - } + sig := ecdsa.SignCompact(priv, crypto.Sha256(msg), false) // ref uncompressed pubkey // remove compact sig recovery code byte at the beginning return sig[1:], nil