github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/reflect.go (about) 1 package amino 2 3 import ( 4 "fmt" 5 "reflect" 6 "unicode" 7 ) 8 9 // ---------------------------------------- 10 // Constants 11 12 var errorType = reflect.TypeOf(new(error)).Elem() 13 14 // ---------------------------------------- 15 // encode: see binary-encode.go and json-encode.go 16 // decode: see binary-decode.go and json-decode.go 17 18 // ---------------------------------------- 19 // Misc. 20 21 // CONTRACT: by the time this is called, len(bz) >= _n 22 // Returns true so you can write one-liners. 23 func slide(bz *[]byte, n *int, _n int) bool { 24 if bz != nil { 25 if _n < 0 || _n > len(*bz) { 26 panic(fmt.Sprintf("impossible slide: len:%v _n:%v", len(*bz), _n)) 27 } 28 *bz = (*bz)[_n:] 29 } 30 if n != nil { 31 *n += _n 32 } 33 return true 34 } 35 36 // maybe dereference if pointer. 37 // drv: the final non-pointer value (which may be invalid). 38 // isPtr: whether rv.Kind() == reflect.Ptr. 39 // isNilPtr: whether a nil pointer at any level. 40 func maybeDerefValue(rv reflect.Value) (drv reflect.Value, rvIsPtr bool, rvIsNilPtr bool) { 41 if rv.Kind() == reflect.Ptr { 42 rvIsPtr = true 43 if rv.IsNil() { 44 rvIsNilPtr = true 45 return 46 } 47 rv = rv.Elem() 48 } 49 drv = rv 50 return 51 } 52 53 // Dereference-and-construct pointers. 54 func maybeDerefAndConstruct(rv reflect.Value) reflect.Value { 55 if rv.Kind() == reflect.Ptr { 56 if rv.IsNil() { 57 newPtr := reflect.New(rv.Type().Elem()) 58 rv.Set(newPtr) 59 } 60 rv = rv.Elem() 61 } 62 if rv.Kind() == reflect.Ptr { 63 panic("unexpected pointer pointer") 64 } 65 return rv 66 } 67 68 // Returns isDefaultValue=true iff is zero and isn't a struct. 69 // NOTE: Also works for Maps, Chans, and Funcs, though they are not 70 // otherwise supported by Amino. For future? 71 func isNonstructDefaultValue(rv reflect.Value) (isDefault bool) { 72 // time.Duration is a special case, 73 // it is considered a struct for encoding purposes. 74 switch rv.Type() { 75 case durationType: 76 return false 77 } 78 // general cae 79 switch rv.Kind() { 80 case reflect.Ptr: 81 if rv.IsNil() { 82 return true 83 } else { 84 erv := rv.Elem() 85 return isNonstructDefaultValue(erv) 86 } 87 case reflect.Bool: 88 return rv.Bool() == false 89 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 90 return rv.Int() == 0 91 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 92 return rv.Uint() == 0 93 case reflect.String: 94 return rv.Len() == 0 95 case reflect.Chan, reflect.Map, reflect.Slice: 96 return rv.IsNil() || rv.Len() == 0 97 case reflect.Func, reflect.Interface: 98 return rv.IsNil() 99 case reflect.Struct: 100 return false 101 default: 102 return false 103 } 104 } 105 106 // Returns the default value of a type. For a time type or a 107 // pointer(s) to time, the default value is not zero (or nil), but the 108 // time value of 1970. 109 // 110 // The default value of a struct pointer is nil, while the default value of 111 // other pointers is not nil. This is due to a proto3 wart, e.g. while there 112 // is a way to distinguish between a nil struct/message vs an empty one (via 113 // its presence or absence in an outer struct), there is no such way to 114 // distinguish between nil bytes/lists and empty bytes/lists, are they are all 115 // absent in binary encoding. 116 func defaultValue(rt reflect.Type) (rv reflect.Value) { 117 switch rt.Kind() { 118 case reflect.Ptr: 119 // Dereference all the way and see if it's a time type. 120 ert := rt.Elem() 121 if ert.Kind() == reflect.Ptr { 122 panic("nested pointers not allowed") 123 } 124 if ert == timeType { 125 // Start from the top and construct pointers as needed. 126 rv = reflect.New(rt.Elem()) 127 // Set to 1970, the whole point of this function. 128 rv.Elem().Set(reflect.ValueOf(emptyTime)) 129 return rv 130 } else if ert.Kind() == reflect.Struct { 131 rv = reflect.Zero(rt) 132 return rv 133 } else { 134 rv = reflect.New(rt.Elem()) 135 return rv 136 } 137 case reflect.Struct: 138 if rt == timeType { 139 // Set to 1970, the whole point of this function. 140 rv = reflect.New(rt).Elem() 141 rv.Set(reflect.ValueOf(emptyTime)) 142 return rv 143 } else { 144 return reflect.Zero(rt) 145 } 146 } 147 148 // Just return the default Go zero object. 149 // Return an empty struct. 150 return reflect.Zero(rt) 151 } 152 153 // NOTE: Also works for Maps and Chans, though they are not 154 // otherwise supported by Amino. For future? 155 func isNil(rv reflect.Value) bool { 156 switch rv.Kind() { 157 case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice: 158 return rv.IsNil() 159 default: 160 return false 161 } 162 } 163 164 // constructConcreteType creates the concrete value as 165 // well as the corresponding settable value for it. 166 // Return irvSet which should be set on caller's interface rv. 167 func constructConcreteType(cinfo *TypeInfo) (crv, irvSet reflect.Value) { 168 // Construct new concrete type. 169 if cinfo.PointerPreferred { 170 cPtrRv := reflect.New(cinfo.Type) 171 crv = cPtrRv.Elem() 172 irvSet = cPtrRv 173 } else { 174 crv = reflect.New(cinfo.Type).Elem() 175 irvSet = crv 176 } 177 return 178 } 179 180 func toReprObject(rv reflect.Value) (rrv reflect.Value, err error) { 181 var mwrm reflect.Value 182 if rv.CanAddr() { 183 mwrm = rv.Addr().MethodByName("MarshalAmino") 184 } else { 185 mwrm = rv.MethodByName("MarshalAmino") 186 } 187 mwouts := mwrm.Call(nil) 188 if !mwouts[1].IsNil() { 189 erri := mwouts[1].Interface() 190 if erri != nil { 191 err = erri.(error) 192 return rrv, err 193 } 194 } 195 rrv = mwouts[0] 196 return 197 } 198 199 func isExported(field reflect.StructField) bool { 200 // Test 1: 201 if field.PkgPath != "" { 202 return false 203 } 204 // Test 2: 205 var first rune 206 for _, c := range field.Name { 207 first = c 208 break 209 } 210 // TODO: JAE: I'm not sure that the unicode spec 211 // is the correct spec to use, so this might be wrong. 212 213 return unicode.IsUpper(first) 214 } 215 216 func marshalAminoReprType(rm reflect.Method) (rrt reflect.Type) { 217 // Verify form of this method. 218 if rm.Type.NumIn() != 1 { 219 panic(fmt.Sprintf("MarshalAmino should have 1 input parameters (including receiver); got %v", rm.Type)) 220 } 221 if rm.Type.NumOut() != 2 { 222 panic(fmt.Sprintf("MarshalAmino should have 2 output parameters; got %v", rm.Type)) 223 } 224 if out := rm.Type.Out(1); out != errorType { 225 panic(fmt.Sprintf("MarshalAmino should have second output parameter of error type, got %v", out)) 226 } 227 rrt = rm.Type.Out(0) 228 if rrt.Kind() == reflect.Ptr { 229 panic(fmt.Sprintf("Representative objects cannot be pointers; got %v", rrt)) 230 } 231 return 232 } 233 234 func unmarshalAminoReprType(rm reflect.Method) (rrt reflect.Type) { 235 // Verify form of this method. 236 if rm.Type.NumIn() != 2 { 237 panic(fmt.Sprintf("UnmarshalAmino should have 2 input parameters (including receiver); got %v", rm.Type)) 238 } 239 if in1 := rm.Type.In(0); in1.Kind() != reflect.Ptr { 240 panic(fmt.Sprintf("UnmarshalAmino first input parameter should be pointer type but got %v", in1)) 241 } 242 if rm.Type.NumOut() != 1 { 243 panic(fmt.Sprintf("UnmarshalAmino should have 1 output parameters; got %v", rm.Type)) 244 } 245 if out := rm.Type.Out(0); out != errorType { 246 panic(fmt.Sprintf("UnmarshalAmino should have first output parameter of error type, got %v", out)) 247 } 248 rrt = rm.Type.In(1) 249 if rrt.Kind() == reflect.Ptr { 250 panic(fmt.Sprintf("Representative objects cannot be pointers; got %v", rrt)) 251 } 252 return 253 } 254 255 // NOTE: do not change this definition. 256 // It is also defined for genproto. 257 func isListType(rt reflect.Type) bool { 258 return rt.Kind() == reflect.Slice || 259 rt.Kind() == reflect.Array 260 }