Skip to content

Commit

Permalink
fix(amino/json): use AminoMarshaler reprType info
Browse files Browse the repository at this point in the history
When a type is registered, if it implements AminoMarshaler, then use the type
returned by `MarshalAmino` method to feed type `ConcreteInfo`.

This prevents a panics that happens during the sanity check phase of
`encodeReflectJSONInterface` when the type `ConcreteInfo` doesn't match
the JSON structure. For instance, that used to happen with `BigintValue`
which is a struct (so expected JSON is `{...}`), but is marshaled into a
simple string (so expected JSON is only `"..."`).
  • Loading branch information
tbruyelle committed Sep 4, 2023
1 parent e90bfd8 commit d10a42b
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 44 deletions.
27 changes: 27 additions & 0 deletions gnovm/pkg/gnolang/package_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package gnolang

import (
"reflect"
"testing"

"github.com/jaekwon/testify/require"

"github.com/gnolang/gno/tm2/pkg/amino"
)

// Tries to reproduce the bug #1036 on all registered types
func TestMarshalAminoRegisteredTypes(t *testing.T) {
for _, typ := range Package.Types {
// Instanciate registered type
x := reflect.New(typ.Type).Interface()
// Call MarshalAmino directly on 'x'
_, err := amino.MarshalJSON(x)
require.NoError(t, err, "marshal type %s", typ.Type.Name())
// Call MarshalAmino on a struct that embeds 'x' in a field of type any
xx := struct {
X any
}{X: x}
_, err = amino.MarshalJSON(xx)
require.NoError(t, err, "marshal type %s from struct", typ.Type.Name())
}
}
22 changes: 0 additions & 22 deletions gnovm/pkg/gnolang/values_test.go

This file was deleted.

9 changes: 6 additions & 3 deletions tm2/pkg/amino/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,13 +634,16 @@ func (cdc *Codec) newTypeInfoUnregisteredWLocked(rt reflect.Type) *TypeInfo {
panic(err)
}
info.ConcreteInfo.ReprType = rinfo
info.ConcreteInfo.IsJSONWellKnownType = rinfo.IsJSONWellKnownType
info.ConcreteInfo.IsJSONAnyValueType = rinfo.IsJSONAnyValueType
info.ConcreteInfo.IsBinaryWellKnownType = rinfo.IsBinaryWellKnownType
} else {
info.ConcreteInfo.IsAminoMarshaler = false
info.ConcreteInfo.ReprType = info
info.ConcreteInfo.IsJSONWellKnownType = isJSONWellKnownType(rt)
info.ConcreteInfo.IsJSONAnyValueType = isJSONAnyValueType(rt)
info.ConcreteInfo.IsBinaryWellKnownType = isBinaryWellKnownType(rt)
}
info.ConcreteInfo.IsBinaryWellKnownType = isBinaryWellKnownType(rt)
info.ConcreteInfo.IsJSONWellKnownType = isJSONWellKnownType(rt)
info.ConcreteInfo.IsJSONAnyValueType = isJSONAnyValueType(rt)
if rt.Kind() == reflect.Array || rt.Kind() == reflect.Slice {
einfo, err := cdc.getTypeInfoWLocked(rt.Elem())
if err != nil {
Expand Down
20 changes: 1 addition & 19 deletions tm2/pkg/amino/json_encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,24 +397,6 @@ func isJSONAnyValueType(rt reflect.Type) bool {
// {@type,value}, the latter specifically
// {@type:"/google.protobuf.Any",value:{@type,value}).
return true
} else {
// Otherwise, it depends on the kind.
switch rt.Kind() {
case
reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64,
// Primitive types get special {@type,value} treatment. In
// binary form, most of these types would be encoded
// wrapped in an implicit struct, except for lists (both of
// bytes and of anything else), and for strings...
reflect.Array, reflect.Slice, reflect.String:
// ...which are all non-objects that must be encoded as
// {@type,value}.
return true
default:
return false
}
return false
}
return false
}

0 comments on commit d10a42b

Please sign in to comment.