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 }