github.com/mavryk-network/mvgo@v1.19.9/micheline/typeinfo.go (about) 1 // Copyright (c) 2022 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 4 package micheline 5 6 import ( 7 "encoding" 8 "fmt" 9 "reflect" 10 "strconv" 11 "strings" 12 "sync" 13 ) 14 15 const tagName = "prim" 16 17 // typeInfo holds details for the representation of a type. 18 type typeInfo struct { 19 fields []fieldInfo 20 } 21 22 // fieldInfo holds details for the representation of a single field. 23 type fieldInfo struct { 24 idx []int // Go struct index 25 name string // field name (= Go struct name) 26 typ OpCode 27 path []int 28 nofail bool 29 } 30 31 func (f fieldInfo) String() string { 32 return fmt.Sprintf("FieldInfo: name=%s typ=%s goloc=%v primloc=%v", f.name, f.typ, f.idx, f.path) 33 } 34 35 var tinfoMap = make(map[reflect.Type]*typeInfo) 36 var tinfoLock sync.RWMutex 37 38 var ( 39 binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() 40 // binaryMarshalerType = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem() 41 textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 42 // textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() 43 primUnmarshalerType = reflect.TypeOf((*PrimUnmarshaler)(nil)).Elem() 44 // primMarshalerType = reflect.TypeOf((*PrimMarshaler)(nil)).Elem() 45 // stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() 46 byteSliceType = reflect.TypeOf([]byte(nil)) 47 szPrim = int(reflect.TypeOf(Prim{}).Size()) 48 ) 49 50 func canTypUnmarshalBinary(typ reflect.Type) bool { 51 return reflect.PointerTo(typ).Implements(binaryUnmarshalerType) || 52 typ.Implements(binaryUnmarshalerType) 53 } 54 55 func canTypUnmarshalText(typ reflect.Type) bool { 56 return reflect.PointerTo(typ).Implements(textUnmarshalerType) || 57 typ.Implements(textUnmarshalerType) 58 } 59 60 // getTypeInfo returns the typeInfo structure with details necessary 61 // for marshaling and unmarshaling of typ. 62 func getTypeInfo(typ reflect.Type) (*typeInfo, error) { 63 tinfoLock.RLock() 64 tinfo, ok := tinfoMap[typ] 65 tinfoLock.RUnlock() 66 if ok { 67 return tinfo, nil 68 } 69 tinfo = &typeInfo{} 70 if typ.Kind() != reflect.Struct { 71 primType, err := mapGoTypeToPrimType(typ) 72 if err != nil { 73 return nil, fmt.Errorf("micheline: %v", err) 74 } 75 tinfo.fields = []fieldInfo{{typ: primType}} 76 return tinfo, nil 77 } 78 n := typ.NumField() 79 for i := 0; i < n; i++ { 80 f := typ.Field(i) 81 if (f.PkgPath != "" && !f.Anonymous) || f.Tag.Get(tagName) == "-" { 82 continue // Private field 83 } 84 85 // For embedded structs, embed their fields. 86 if f.Anonymous { 87 t := f.Type 88 if t.Kind() == reflect.Ptr { 89 t = t.Elem() 90 } 91 if t.Kind() == reflect.Struct { 92 inner, err := getTypeInfo(t) 93 if err != nil { 94 return nil, err 95 } 96 for _, finfo := range inner.fields { 97 finfo.idx = append([]int{i}, finfo.idx...) 98 if err := addFieldInfo(typ, tinfo, &finfo); err != nil { 99 return nil, err 100 } 101 } 102 continue 103 } 104 } 105 106 finfo, err := structFieldInfo(&f) 107 if err != nil { 108 return nil, err 109 } 110 111 // Add the field if it doesn't conflict with other fields. 112 if err := addFieldInfo(typ, tinfo, finfo); err != nil { 113 return nil, err 114 } 115 } 116 tinfoLock.Lock() 117 tinfoMap[typ] = tinfo 118 tinfoLock.Unlock() 119 return tinfo, nil 120 } 121 122 // structFieldInfo builds and returns a fieldInfo for f. 123 func structFieldInfo(f *reflect.StructField) (*fieldInfo, error) { 124 finfo := &fieldInfo{ 125 idx: f.Index, 126 name: f.Name, 127 } 128 tag := f.Tag.Get(tagName) 129 130 // Parse struct tag 131 tokens := strings.Split(tag, ",") 132 if len(tokens) > 1 { 133 finfo.name = tokens[0] 134 for _, flag := range tokens[1:] { 135 ff := strings.Split(flag, "=") 136 switch ff[0] { 137 case "path": 138 for _, v := range strings.Split(strings.TrimSuffix(strings.TrimPrefix(ff[1], "/"), "/"), "/") { 139 i, err := strconv.Atoi(v) 140 if err != nil { 141 return nil, fmt.Errorf("micheline: invalid path %q in field %s: %v", ff[1], f.Name, err) 142 } 143 finfo.path = append(finfo.path, i) 144 } 145 case "nofail": 146 finfo.nofail = true 147 } 148 } 149 } 150 if typ, err := mapGoTypeToPrimType(f.Type); err != nil { 151 return nil, fmt.Errorf("micheline: field %s: %v", finfo.name, err) 152 } else { 153 finfo.typ = typ 154 } 155 156 return finfo, nil 157 } 158 159 func mapGoTypeToPrimType(typ reflect.Type) (oc OpCode, err error) { 160 switch typ.Kind() { 161 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 162 oc = T_NAT 163 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 164 oc = T_INT 165 case reflect.Slice: 166 switch { 167 case canTypUnmarshalBinary(typ): 168 oc = T_BYTES 169 case canTypUnmarshalText(typ): 170 oc = T_STRING 171 case typ == byteSliceType: 172 oc = T_BYTES 173 default: 174 oc = T_LIST 175 } 176 case reflect.Map: 177 oc = T_MAP 178 case reflect.String: 179 oc = T_STRING 180 case reflect.Bool: 181 oc = T_BOOL 182 case reflect.Array: 183 switch typ.String() { 184 case "mavryk.Address": 185 oc = T_ADDRESS 186 case "mavryk.ChainIdHash": 187 oc = T_CHAIN_ID 188 default: 189 if canTypUnmarshalBinary(typ) { 190 oc = T_BYTES 191 } else { 192 err = fmt.Errorf("unsupported embedded array type %s", typ.String()) 193 } 194 } 195 case reflect.Struct: 196 switch typ.String() { 197 case "time.Time": 198 oc = T_TIMESTAMP 199 case "mavryk.Z": 200 oc = T_NAT 201 case "mavryk.N": 202 oc = T_NAT 203 case "mavryk.Key": 204 oc = T_KEY 205 case "mavryk.Signature": 206 oc = T_SIGNATURE 207 default: 208 if canTypUnmarshalBinary(typ) { 209 oc = T_BYTES 210 } else { 211 err = fmt.Errorf("unsupported embedded struct type %s", typ.String()) 212 } 213 } 214 default: 215 err = fmt.Errorf("unsupported type %s (%v)", typ.String(), typ.Kind()) 216 } 217 return 218 } 219 220 func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error { 221 var conflicts []int 222 // Find all conflicts. 223 for i := range tinfo.fields { 224 oldf := &tinfo.fields[i] 225 if newf.name == oldf.name { 226 conflicts = append(conflicts, i) 227 } 228 } 229 230 // Return the first error. 231 for _, i := range conflicts { 232 oldf := &tinfo.fields[i] 233 f1 := typ.FieldByIndex(oldf.idx) 234 f2 := typ.FieldByIndex(newf.idx) 235 return fmt.Errorf("micheline: %s field %q with tag %q conflicts with field %q with tag %q", typ, f1.Name, f1.Tag.Get(tagName), f2.Name, f2.Tag.Get(tagName)) 236 } 237 238 // Without conflicts, add the new field and return. 239 tinfo.fields = append(tinfo.fields, *newf) 240 return nil 241 } 242 243 // value returns v's field value corresponding to finfo. 244 // It's equivalent to v.FieldByIndex(finfo.idx), but initializes 245 // and dereferences pointers as necessary. 246 func (finfo *fieldInfo) value(v reflect.Value) reflect.Value { 247 for i, x := range finfo.idx { 248 if i > 0 { 249 t := v.Type() 250 if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct { 251 if v.IsNil() { 252 v.Set(reflect.New(v.Type().Elem())) 253 } 254 v = v.Elem() 255 } 256 } 257 v = v.Field(x) 258 } 259 260 return v 261 } 262 263 func derefValue(val reflect.Value) reflect.Value { 264 if val.Kind() == reflect.Interface && !val.IsNil() { 265 e := val.Elem() 266 if e.Kind() == reflect.Ptr && !e.IsNil() { 267 val = e 268 } 269 } 270 271 if val.Kind() == reflect.Ptr { 272 if val.IsNil() { 273 val.Set(reflect.New(val.Type().Elem())) 274 } 275 val = val.Elem() 276 } 277 return val 278 } 279 280 func indirectType(typ reflect.Type) reflect.Type { 281 if typ.Kind() == reflect.Ptr { 282 val := reflect.New(typ.Elem()) 283 return val.Elem().Type() 284 } 285 return typ 286 }