github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/json_decode.go (about) 1 package amino 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "reflect" 8 "strings" 9 10 "github.com/gnolang/gno/tm2/pkg/errors" 11 ) 12 13 // ---------------------------------------- 14 // cdc.decodeReflectJSON 15 16 // CONTRACT: rv.CanAddr() is true. 17 func (cdc *Codec) decodeReflectJSON(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) { 18 if !rv.CanAddr() { 19 panic("rv not addressable") 20 } 21 if info.Type.Kind() == reflect.Interface && rv.Kind() == reflect.Ptr { 22 panic("should not happen") 23 } 24 if printLog { 25 fmt.Printf("(D) decodeReflectJSON(bz: %s, info: %v, rv: %#v (%v), fopts: %v)\n", 26 bz, info, rv.Interface(), rv.Type(), fopts) 27 defer func() { 28 fmt.Printf("(D) -> err: %v\n", err) 29 }() 30 } 31 32 // Special case for null for either interface, pointer, slice 33 // NOTE: This doesn't match the binary implementation completely. 34 if nullBytes(bz) { 35 rv.Set(defaultValue(rv.Type())) 36 return 37 } 38 39 // Dereference-and-construct if pointer. 40 rv = maybeDerefAndConstruct(rv) 41 42 // Handle the most special case, "well known". 43 if info.ConcreteInfo.IsJSONWellKnownType { 44 var ok bool 45 ok, err = decodeReflectJSONWellKnown(bz, info, rv, fopts) 46 if ok || err != nil { 47 return 48 } 49 } 50 51 // Handle override if a pointer to rv implements UnmarshalAmino. 52 if info.IsAminoMarshaler { 53 // First, decode repr instance from bytes. 54 rrv := reflect.New(info.ReprType.Type).Elem() 55 rinfo := info.ReprType 56 err = cdc.decodeReflectJSON(bz, rinfo, rrv, fopts) 57 if err != nil { 58 return 59 } 60 // Then, decode from repr instance. 61 uwrm := rv.Addr().MethodByName("UnmarshalAmino") 62 uwouts := uwrm.Call([]reflect.Value{rrv}) 63 erri := uwouts[0].Interface() 64 if erri != nil { 65 err = erri.(error) 66 } 67 return 68 } 69 70 switch ikind := info.Type.Kind(); ikind { 71 // ---------------------------------------- 72 // Complex 73 74 case reflect.Interface: 75 err = cdc.decodeReflectJSONInterface(bz, info, rv, fopts) 76 77 case reflect.Array: 78 err = cdc.decodeReflectJSONArray(bz, info, rv, fopts) 79 80 case reflect.Slice: 81 err = cdc.decodeReflectJSONSlice(bz, info, rv, fopts) 82 83 case reflect.Struct: 84 err = cdc.decodeReflectJSONStruct(bz, info, rv, fopts) 85 86 // ---------------------------------------- 87 // Signed, Unsigned 88 89 case reflect.Int64, reflect.Int: 90 fallthrough 91 case reflect.Uint64, reflect.Uint: 92 if bz[0] != '"' || bz[len(bz)-1] != '"' { 93 err = errors.New( 94 "invalid character -- Amino:JSON int/int64/uint/uint64 expects quoted values for javascript numeric support, got: %v", //nolint: lll 95 string(bz), 96 ) 97 if err != nil { 98 return 99 } 100 } 101 bz = bz[1 : len(bz)-1] 102 fallthrough 103 case reflect.Int32, reflect.Int16, reflect.Int8, 104 reflect.Uint32, reflect.Uint16, reflect.Uint8: 105 err = invokeStdlibJSONUnmarshal(bz, rv, fopts) 106 107 // ---------------------------------------- 108 // Misc 109 110 case reflect.Float32, reflect.Float64: 111 if !fopts.Unsafe { 112 return errors.New("amino:JSON float* support requires `amino:\"unsafe\"`") 113 } 114 fallthrough 115 case reflect.Bool, reflect.String: 116 err = invokeStdlibJSONUnmarshal(bz, rv, fopts) 117 118 // ---------------------------------------- 119 // Default 120 121 default: 122 panic(fmt.Sprintf("unsupported type %v", info.Type.Kind())) 123 } 124 125 return err 126 } 127 128 func invokeStdlibJSONUnmarshal(bz []byte, rv reflect.Value, fopts FieldOptions) error { 129 if !rv.CanAddr() && rv.Kind() != reflect.Ptr { 130 panic("rv not addressable nor pointer") 131 } 132 133 rrv := rv 134 if rv.Kind() != reflect.Ptr { 135 rrv = reflect.New(rv.Type()) 136 } 137 138 if err := json.Unmarshal(bz, rrv.Interface()); err != nil { 139 return err 140 } 141 rv.Set(rrv.Elem()) 142 return nil 143 } 144 145 // CONTRACT: rv.CanAddr() is true. 146 func (cdc *Codec) decodeReflectJSONInterface(bz []byte, iinfo *TypeInfo, rv reflect.Value, 147 fopts FieldOptions, 148 ) (err error) { 149 if !rv.CanAddr() { 150 panic("rv not addressable") 151 } 152 if printLog { 153 fmt.Println("(d) decodeReflectJSONInterface") 154 defer func() { 155 fmt.Printf("(d) -> err: %v\n", err) 156 }() 157 } 158 159 /* 160 We don't make use of user-provided interface values because there are a 161 lot of edge cases. 162 163 * What if the type is mismatched? 164 * What if the JSON field entry is missing? 165 * Circular references? 166 */ 167 if !rv.IsNil() { 168 // We don't strictly need to set it nil, but lets keep it here for a 169 // while in case we forget, for defensive purposes. 170 rv.Set(iinfo.ZeroValue) 171 } 172 173 // Extract type_url. 174 typeURL, value, err := extractJSONTypeURL(bz) 175 if err != nil { 176 return 177 } 178 179 // NOTE: Unlike decodeReflectBinaryInterface, we already dealt with nil in decodeReflectJSON. 180 181 // Get concrete type info. 182 // NOTE: Unlike decodeReflectBinaryInterface, uses the full type_url string, 183 // which if generated by Amino, is the name preceded by a single slash. 184 var cinfo *TypeInfo 185 cinfo, err = cdc.getTypeInfoFromTypeURLRLock(typeURL, fopts) 186 if err != nil { 187 return 188 } 189 190 // Extract the value bytes. 191 if cinfo.IsJSONAnyValueType || (cinfo.IsAminoMarshaler && cinfo.ReprType.IsJSONAnyValueType) { 192 bz = value 193 } else { 194 bz, err = deriveJSONObject(bz, typeURL) 195 if err != nil { 196 return 197 } 198 } 199 200 // Construct the concrete type. 201 crv, irvSet := constructConcreteType(cinfo) 202 203 // Decode into the concrete type. 204 err = cdc.decodeReflectJSON(bz, cinfo, crv, fopts) 205 if err != nil { 206 rv.Set(irvSet) // Helps with debugging 207 return 208 } 209 210 // We need to set here, for when !PointerPreferred and the type 211 // is say, an array of bytes (e.g. [32]byte), then we must call 212 // rv.Set() *after* the value was acquired. 213 rv.Set(irvSet) 214 return err 215 } 216 217 // CONTRACT: rv.CanAddr() is true. 218 func (cdc *Codec) decodeReflectJSONArray(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) { 219 if !rv.CanAddr() { 220 panic("rv not addressable") 221 } 222 if printLog { 223 fmt.Println("(d) decodeReflectJSONArray") 224 defer func() { 225 fmt.Printf("(d) -> err: %v\n", err) 226 }() 227 } 228 ert := info.Type.Elem() 229 length := info.Type.Len() 230 231 switch ert.Kind() { 232 case reflect.Uint8: // Special case: byte array 233 var buf []byte 234 err = json.Unmarshal(bz, &buf) 235 if err != nil { 236 return 237 } 238 if len(buf) != length { 239 err = fmt.Errorf("decodeReflectJSONArray: byte-length mismatch, got %v want %v", 240 len(buf), length) 241 } 242 reflect.Copy(rv, reflect.ValueOf(buf)) 243 return 244 245 default: // General case. 246 var einfo *TypeInfo 247 einfo, err = cdc.getTypeInfoWLock(ert) 248 if err != nil { 249 return 250 } 251 252 // Read into rawSlice. 253 var rawSlice []json.RawMessage 254 if err = json.Unmarshal(bz, &rawSlice); err != nil { 255 return 256 } 257 if len(rawSlice) != length { 258 err = fmt.Errorf("decodeReflectJSONArray: length mismatch, got %v want %v", len(rawSlice), length) 259 return 260 } 261 262 // Decode each item in rawSlice. 263 for i := 0; i < length; i++ { 264 erv := rv.Index(i) 265 ebz := rawSlice[i] 266 err = cdc.decodeReflectJSON(ebz, einfo, erv, fopts) 267 if err != nil { 268 return 269 } 270 } 271 return 272 } 273 } 274 275 // CONTRACT: rv.CanAddr() is true. 276 func (cdc *Codec) decodeReflectJSONSlice(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) { 277 if !rv.CanAddr() { 278 panic("rv not addressable") 279 } 280 if printLog { 281 fmt.Println("(d) decodeReflectJSONSlice") 282 defer func() { 283 fmt.Printf("(d) -> err: %v\n", err) 284 }() 285 } 286 287 ert := info.Type.Elem() 288 289 switch ert.Kind() { 290 case reflect.Uint8: // Special case: byte slice 291 err = json.Unmarshal(bz, rv.Addr().Interface()) 292 if err != nil { 293 return 294 } 295 if rv.Len() == 0 { 296 // Special case when length is 0. 297 // NOTE: We prefer nil slices. 298 rv.Set(info.ZeroValue) 299 } 300 // else { 301 // NOTE: Already set via json.Unmarshal() above. 302 // } 303 return 304 305 default: // General case. 306 var einfo *TypeInfo 307 einfo, err = cdc.getTypeInfoWLock(ert) 308 if err != nil { 309 return 310 } 311 312 // Read into rawSlice. 313 var rawSlice []json.RawMessage 314 if err = json.Unmarshal(bz, &rawSlice); err != nil { 315 return 316 } 317 318 // Special case when length is 0. 319 // NOTE: We prefer nil slices. 320 length := len(rawSlice) 321 if length == 0 { 322 rv.Set(info.ZeroValue) 323 return 324 } 325 326 // Read into a new slice. 327 esrt := reflect.SliceOf(ert) // TODO could be optimized. 328 srv := reflect.MakeSlice(esrt, length, length) 329 for i := 0; i < length; i++ { 330 erv := srv.Index(i) 331 ebz := rawSlice[i] 332 err = cdc.decodeReflectJSON(ebz, einfo, erv, fopts) 333 if err != nil { 334 return 335 } 336 } 337 338 // TODO do we need this extra step? 339 rv.Set(srv) 340 return 341 } 342 } 343 344 // CONTRACT: rv.CanAddr() is true. 345 func (cdc *Codec) decodeReflectJSONStruct(bz []byte, info *TypeInfo, rv reflect.Value, fopts FieldOptions) (err error) { 346 if !rv.CanAddr() { 347 panic("rv not addressable") 348 } 349 if printLog { 350 fmt.Println("(d) decodeReflectJSONStruct") 351 defer func() { 352 fmt.Printf("(d) -> err: %v\n", err) 353 }() 354 } 355 356 // Map all the fields(keys) to their blobs/bytes. 357 // NOTE: In decodeReflectBinaryStruct, we don't need to do this, 358 // since fields are encoded in order. 359 rawMap := make(map[string]json.RawMessage) 360 err = json.Unmarshal(bz, &rawMap) 361 if err != nil { 362 return 363 } 364 365 for _, field := range info.Fields { 366 // Get field rv and info. 367 frv := rv.Field(field.Index) 368 finfo := field.TypeInfo 369 370 // Get value from rawMap. 371 valueBytes := rawMap[field.JSONName] 372 if len(valueBytes) == 0 { 373 // TODO: Since the Go stdlib's JSON codec allows case-insensitive 374 // keys perhaps we need to also do case-insensitive lookups here. 375 // So "Vanilla" and "vanilla" would both match to the same field. 376 // It is actually a security flaw with encoding/json library 377 // - See https://github.com/golang/go/issues/14750 378 // but perhaps we are aiming for as much compatibility here. 379 // JAE: I vote we depart from encoding/json, than carry a vuln. 380 381 // Set to the zero value only if not omitempty 382 if !field.JSONOmitEmpty { 383 // Set nil/zero on frv. 384 frv.Set(defaultValue(frv.Type())) 385 } 386 387 continue 388 } 389 390 // Decode into field rv. 391 err = cdc.decodeReflectJSON(valueBytes, finfo, frv, fopts) 392 if err != nil { 393 return 394 } 395 } 396 397 return nil 398 } 399 400 // ---------------------------------------- 401 // Misc. 402 403 type anyWrapper struct { 404 TypeURL string `json:"@type"` 405 Value json.RawMessage `json:"value"` 406 } 407 408 func extractJSONTypeURL(bz []byte) (typeURL string, value json.RawMessage, err error) { 409 anyw := new(anyWrapper) 410 err = json.Unmarshal(bz, anyw) 411 if err != nil { 412 err = fmt.Errorf("cannot parse Any JSON wrapper: %w", err) 413 return 414 } 415 416 // Get typeURL. 417 if anyw.TypeURL == "" { 418 err = errors.New("JSON encoding of interfaces require non-empty @type field") 419 return 420 } 421 typeURL = anyw.TypeURL 422 value = anyw.Value 423 return 424 } 425 426 func deriveJSONObject(bz []byte, typeURL string) (res []byte, err error) { 427 str := string(bz) 428 if len(bz) == 0 { 429 err = errors.New("expected JSON object but was empty") 430 return 431 } 432 if !strings.HasPrefix(str, "{") { 433 err = fmt.Errorf("expected JSON object but was not: %s", bz) 434 return 435 } 436 str = strings.TrimLeft(str, " \t\r\n") 437 if !strings.HasPrefix(str, "{") { 438 err = fmt.Errorf("expected JSON object representing Any to start with '{', but got %v", string(bz)) 439 return 440 } 441 str = str[1:] 442 str = strings.TrimLeft(str, " \t\r\n") 443 if !strings.HasPrefix(str, `"@type"`) { 444 err = fmt.Errorf("expected JSON object representing Any to start with \"@type\" field, but got %v", string(bz)) 445 return 446 } 447 str = str[7:] 448 str = strings.TrimLeft(str, " \t\r\n") 449 if !strings.HasPrefix(str, ":") { 450 err = fmt.Errorf("expected JSON object representing Any to start with \"@type\" field, but got %v", string(bz)) 451 return 452 } 453 str = str[1:] 454 str = strings.TrimLeft(str, " \t\r\n") 455 if !strings.HasPrefix(str, fmt.Sprintf(`"%v"`, typeURL)) { 456 err = fmt.Errorf("expected JSON object representing Any to start with \"@type\":\"%v\", but got %v", typeURL, string(bz)) 457 return 458 } 459 str = str[2+len(typeURL):] 460 str = strings.TrimLeft(str, ",") 461 return []byte("{" + str), nil 462 } 463 464 func nullBytes(b []byte) bool { 465 return bytes.Equal(b, []byte(`null`)) 466 } 467 468 func unquoteString(in string) (out string, err error) { 469 err = json.Unmarshal([]byte(in), &out) 470 return out, err 471 }