github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/json_encode.go (about)

     1  package amino
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  
    10  	"github.com/gnolang/gno/tm2/pkg/errors"
    11  )
    12  
    13  // ----------------------------------------
    14  // cdc.encodeReflectJSON
    15  
    16  // This is the main entrypoint for encoding all types in json form.  This
    17  // function calls encodeReflectJSON*, and generally those functions should
    18  // only call this one, for the disfix wrapper is only written here.
    19  // NOTE: Unlike encodeReflectBinary, rv may be a pointer.  This is because
    20  // unlike the binary representation, in JSON there is a concrete representation
    21  // of no value -- null.  So, a nil pointer here encodes as null, whereas
    22  // encodeReflectBinary() assumes that the pointer is already dereferenced.
    23  // CONTRACT: rv is valid.
    24  func (cdc *Codec) encodeReflectJSON(w io.Writer, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) {
    25  	if !rv.IsValid() {
    26  		panic("should not happen")
    27  	}
    28  	if printLog {
    29  		fmt.Printf("(E) encodeReflectJSON(info: %v, rv: %#v (%v), fopts: %v)\n",
    30  			info, rv.Interface(), rv.Type(), fopts)
    31  		defer func() {
    32  			fmt.Printf("(E) -> err: %v\n", err)
    33  		}()
    34  	}
    35  
    36  	// Dereference value if pointer.
    37  	if rv.Kind() == reflect.Ptr {
    38  		if rv.IsNil() {
    39  			err = writeStr(w, `null`)
    40  			return
    41  		}
    42  		rv = rv.Elem()
    43  	}
    44  
    45  	// Handle the most special case, "well known".
    46  	if info.IsJSONWellKnownType {
    47  		var ok bool
    48  		ok, err = encodeReflectJSONWellKnown(w, info, rv, fopts)
    49  		if ok || err != nil {
    50  			return
    51  		}
    52  	}
    53  
    54  	// Handle override if rv implements amino.Marshaler.
    55  	if info.IsAminoMarshaler {
    56  		// First, encode rv into repr instance.
    57  		var (
    58  			rrv   reflect.Value
    59  			rinfo *TypeInfo
    60  		)
    61  		rrv, err = toReprObject(rv)
    62  		if err != nil {
    63  			return
    64  		}
    65  		rinfo = info.ReprType
    66  		// Then, encode the repr instance.
    67  		err = cdc.encodeReflectJSON(w, rinfo, rrv, fopts)
    68  		return
    69  	}
    70  
    71  	switch info.Type.Kind() {
    72  	// ----------------------------------------
    73  	// Complex
    74  
    75  	case reflect.Interface:
    76  		return cdc.encodeReflectJSONInterface(w, info, rv, fopts)
    77  
    78  	case reflect.Array, reflect.Slice:
    79  		return cdc.encodeReflectJSONList(w, info, rv, fopts)
    80  
    81  	case reflect.Struct:
    82  		return cdc.encodeReflectJSONStruct(w, info, rv, fopts)
    83  
    84  	// ----------------------------------------
    85  	// Signed, Unsigned
    86  
    87  	case reflect.Int64, reflect.Int:
    88  		_, err = fmt.Fprintf(w, `"%d"`, rv.Int()) // JS can't handle int64
    89  		return
    90  
    91  	case reflect.Uint64, reflect.Uint:
    92  		_, err = fmt.Fprintf(w, `"%d"`, rv.Uint()) // JS can't handle uint64
    93  		return
    94  
    95  	case reflect.Int32, reflect.Int16, reflect.Int8,
    96  		reflect.Uint32, reflect.Uint16, reflect.Uint8:
    97  		return invokeStdlibJSONMarshal(w, rv.Interface())
    98  
    99  	// ----------------------------------------
   100  	// Misc
   101  
   102  	case reflect.Float64, reflect.Float32:
   103  		if !fopts.Unsafe {
   104  			return errors.New("amino.JSON float* support requires `amino:\"unsafe\"`")
   105  		}
   106  		fallthrough
   107  	case reflect.Bool, reflect.String:
   108  		return invokeStdlibJSONMarshal(w, rv.Interface())
   109  
   110  	// ----------------------------------------
   111  	// Default
   112  
   113  	default:
   114  		panic(fmt.Sprintf("unsupported type %v", info.Type.Kind()))
   115  	}
   116  }
   117  
   118  func (cdc *Codec) encodeReflectJSONInterface(w io.Writer, iinfo *TypeInfo, rv reflect.Value,
   119  	fopts FieldOptions,
   120  ) (err error) {
   121  	if printLog {
   122  		fmt.Println("(e) encodeReflectJSONInterface")
   123  		defer func() {
   124  			fmt.Printf("(e) -> err: %v\n", err)
   125  		}()
   126  	}
   127  
   128  	// Special case when rv is nil, just write "null".
   129  	if rv.IsNil() {
   130  		err = writeStr(w, `null`)
   131  		return
   132  	}
   133  
   134  	// Get concrete non-pointer reflect value & type.
   135  	crv := rv.Elem()
   136  	_, crvIsPtr, crvIsNilPtr := maybeDerefValue(crv)
   137  	if crvIsPtr && crv.Kind() == reflect.Interface {
   138  		// See "MARKER: No interface-pointers" in codec.go
   139  		panic("should not happen")
   140  	}
   141  	if crvIsNilPtr {
   142  		panic(fmt.Sprintf("Illegal nil-pointer of type %v for registered interface %v. "+
   143  			"For compatibility with other languages, nil-pointer interface values are forbidden.", crv.Type(), iinfo.Type))
   144  	}
   145  	crt := crv.Type()
   146  
   147  	// Get *TypeInfo for concrete type.
   148  	var cinfo *TypeInfo
   149  	cinfo, err = cdc.getTypeInfoWLock(crt)
   150  	if err != nil {
   151  		return
   152  	}
   153  	if !cinfo.Registered {
   154  		err = errors.New("cannot encode unregistered concrete type %v", crt)
   155  		return
   156  	}
   157  
   158  	// Write Value to buffer
   159  	buf := new(bytes.Buffer)
   160  	cdc.encodeReflectJSON(buf, cinfo, crv, fopts)
   161  	value := buf.Bytes()
   162  	if len(value) == 0 {
   163  		err = errors.New("JSON bytes cannot be empty")
   164  		return
   165  	}
   166  	if cinfo.IsJSONAnyValueType || (cinfo.IsAminoMarshaler && cinfo.ReprType.IsJSONAnyValueType) {
   167  		// Sanity check
   168  		if value[0] == '{' || value[len(value)-1] == '}' {
   169  			err = errors.New("unexpected JSON object %s", value)
   170  			return
   171  		}
   172  		// Write TypeURL
   173  		err = writeStr(w, _fmt(`{"@type":"%s","value":`, cinfo.TypeURL))
   174  		if err != nil {
   175  			return
   176  		}
   177  		// Write Value
   178  		err = writeStr(w, string(value))
   179  		if err != nil {
   180  			return
   181  		}
   182  		// Write closing brace.
   183  		err = writeStr(w, `}`)
   184  		return
   185  	} else {
   186  		// Sanity check
   187  		if value[0] != '{' || value[len(value)-1] != '}' {
   188  			err = errors.New("expected JSON object but got %s", value)
   189  			return
   190  		}
   191  		// Write TypeURL
   192  		err = writeStr(w, _fmt(`{"@type":"%s"`, cinfo.TypeURL))
   193  		if err != nil {
   194  			return
   195  		}
   196  		// Write Value
   197  		if len(value) > 2 {
   198  			err = writeStr(w, ","+string(value[1:]))
   199  		} else {
   200  			err = writeStr(w, `}`)
   201  		}
   202  		return
   203  	}
   204  }
   205  
   206  func (cdc *Codec) encodeReflectJSONList(w io.Writer, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) {
   207  	if printLog {
   208  		fmt.Println("(e) encodeReflectJSONList")
   209  		defer func() {
   210  			fmt.Printf("(e) -> err: %v\n", err)
   211  		}()
   212  	}
   213  
   214  	// Special case when list is a nil slice, just write "null".
   215  	// Empty slices and arrays are not encoded as "null".
   216  	if rv.Kind() == reflect.Slice && rv.IsNil() {
   217  		err = writeStr(w, `null`)
   218  		return
   219  	}
   220  
   221  	ert := info.Type.Elem()
   222  	length := rv.Len()
   223  
   224  	switch ert.Kind() {
   225  	case reflect.Uint8: // Special case: byte array
   226  		// Write bytes in base64.
   227  		// NOTE: Base64 encoding preserves the exact original number of bytes.
   228  		// Get readable slice of bytes.
   229  		var bz []byte
   230  		if rv.CanAddr() {
   231  			bz = rv.Slice(0, length).Bytes()
   232  		} else {
   233  			bz = make([]byte, length)
   234  			reflect.Copy(reflect.ValueOf(bz), rv) // XXX: looks expensive!
   235  		}
   236  		var jsonBytes []byte
   237  		jsonBytes, err = json.Marshal(bz) // base64 encode
   238  		if err != nil {
   239  			return
   240  		}
   241  		_, err = w.Write(jsonBytes)
   242  		return
   243  
   244  	default:
   245  		// Open square bracket.
   246  		err = writeStr(w, `[`)
   247  		if err != nil {
   248  			return
   249  		}
   250  
   251  		// Write elements with comma.
   252  		var einfo *TypeInfo
   253  		einfo, err = cdc.getTypeInfoWLock(ert)
   254  		if err != nil {
   255  			return
   256  		}
   257  		for i := 0; i < length; i++ {
   258  			// Get dereferenced element value and info.
   259  			erv := rv.Index(i)
   260  			if erv.Kind() == reflect.Ptr &&
   261  				erv.IsNil() {
   262  				// then
   263  				err = writeStr(w, `null`)
   264  			} else {
   265  				err = cdc.encodeReflectJSON(w, einfo, erv, fopts)
   266  			}
   267  			if err != nil {
   268  				return
   269  			}
   270  			// Add a comma if it isn't the last item.
   271  			if i != length-1 {
   272  				err = writeStr(w, `,`)
   273  				if err != nil {
   274  					return
   275  				}
   276  			}
   277  		}
   278  
   279  		// Close square bracket.
   280  		defer func() {
   281  			err = writeStr(w, `]`)
   282  		}()
   283  		return
   284  	}
   285  }
   286  
   287  func (cdc *Codec) encodeReflectJSONStruct(w io.Writer, info *TypeInfo, rv reflect.Value, _ FieldOptions) (err error) {
   288  	if printLog {
   289  		fmt.Println("(e) encodeReflectJSONStruct")
   290  		defer func() {
   291  			fmt.Printf("(e) -> err: %v\n", err)
   292  		}()
   293  	}
   294  
   295  	// Part 1.
   296  	err = writeStr(w, `{`)
   297  	if err != nil {
   298  		return
   299  	}
   300  	// Part 2.
   301  	defer func() {
   302  		if err == nil {
   303  			err = writeStr(w, `}`)
   304  		}
   305  	}()
   306  
   307  	writeComma := false
   308  	for _, field := range info.Fields {
   309  		finfo := field.TypeInfo
   310  		// Get dereferenced field value and info.
   311  		frv, _, frvIsNil := maybeDerefValue(rv.Field(field.Index))
   312  		// If frv is empty and omitempty, skip it.
   313  		// NOTE: Unlike Amino:binary, we don't skip null fields unless "omitempty".
   314  		if field.JSONOmitEmpty && isJSONEmpty(frv, field.ZeroValue) {
   315  			continue
   316  		}
   317  		// Now we know we're going to write something.
   318  		// Add a comma if we need to.
   319  		if writeComma {
   320  			err = writeStr(w, `,`)
   321  			if err != nil {
   322  				return
   323  			}
   324  			writeComma = false //nolint:ineffassign
   325  		}
   326  		// Write field JSON name.
   327  		err = invokeStdlibJSONMarshal(w, field.JSONName)
   328  		if err != nil {
   329  			return
   330  		}
   331  		// Write colon.
   332  		err = writeStr(w, `:`)
   333  		if err != nil {
   334  			return
   335  		}
   336  		// Write field value.
   337  		if frvIsNil {
   338  			err = writeStr(w, `null`)
   339  		} else {
   340  			err = cdc.encodeReflectJSON(w, finfo, frv, field.FieldOptions)
   341  		}
   342  		if err != nil {
   343  			return
   344  		}
   345  		writeComma = true
   346  	}
   347  	return err
   348  }
   349  
   350  // ----------------------------------------
   351  // Misc.
   352  
   353  func invokeStdlibJSONMarshal(w io.Writer, v interface{}) error {
   354  	// Note: Please don't stream out the output because that adds a newline
   355  	// using json.NewEncoder(w).Encode(data)
   356  	// as per https://golang.org/pkg/encoding/json/#Encoder.Encode
   357  	blob, err := json.Marshal(v)
   358  	if err != nil {
   359  		return err
   360  	}
   361  	_, err = w.Write(blob)
   362  	return err
   363  }
   364  
   365  func writeStr(w io.Writer, s string) (err error) {
   366  	_, err = w.Write([]byte(s))
   367  	return
   368  }
   369  
   370  func _fmt(s string, args ...interface{}) string {
   371  	return fmt.Sprintf(s, args...)
   372  }
   373  
   374  // For json:",omitempty".
   375  // Returns true for zero values, but also non-nil zero-length slices and strings.
   376  func isJSONEmpty(rv reflect.Value, zrv reflect.Value) bool {
   377  	if !rv.IsValid() {
   378  		return true
   379  	}
   380  	if reflect.DeepEqual(rv.Interface(), zrv.Interface()) {
   381  		return true
   382  	}
   383  	switch rv.Kind() {
   384  	case reflect.Slice, reflect.Array, reflect.String:
   385  		if rv.Len() == 0 {
   386  			return true
   387  		}
   388  	}
   389  	return false
   390  }
   391  
   392  func isJSONAnyValueType(rt reflect.Type) bool {
   393  	if isJSONWellKnownType(rt) {
   394  		// All well known types are to be encoded as "{@type,value}" in
   395  		// JSON.  Some of these may be structs/objects, such as
   396  		// gAnyType, but nevertheless they must be encoded as
   397  		// {@type,value}, the latter specifically
   398  		// {@type:"/google.protobuf.Any",value:{@type,value}).
   399  		return true
   400  	}
   401  	// Otherwise, it depends on the kind.
   402  	switch rt.Kind() {
   403  	case
   404  		reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
   405  		reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,
   406  		reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64,
   407  		// Primitive types get special {@type,value} treatment.  In
   408  		// binary form, most of these types would be encoded
   409  		// wrapped in an implicit struct, except for lists (both of
   410  		// bytes and of anything else), and for strings...
   411  		reflect.Array, reflect.Slice, reflect.String:
   412  		// ...which are all non-objects that must be encoded as
   413  		// {@type,value}.
   414  		return true
   415  	default:
   416  		return false
   417  	}
   418  }