1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2017-2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * struct.go 12 * 13 * Created on May 07, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package xreflect 18 19import ( 20 "fmt" 21 "go/ast" 22 "go/token" 23 "go/types" 24 "reflect" 25) 26 27// Field returns a struct type's i'th field. 28// It panics if the type's Kind is not Struct. 29// It panics if i is not in the range [0, NumField()). 30func (t *xtype) Field(i int) StructField { 31 if t.kind != reflect.Struct { 32 xerrorf(t, "Field of non-struct type %v", t) 33 } 34 v := t.universe 35 if v.ThreadSafe { 36 defer un(lock(v)) 37 } 38 return t.field(i) 39} 40 41func (t *xtype) field(i int) StructField { 42 if t.kind != reflect.Struct { 43 xerrorf(t, "Field of non-struct type %v", t) 44 } 45 gtype := t.gtype.Underlying().(*types.Struct) 46 47 if i < 0 || i >= gtype.NumFields() { 48 xerrorf(t, "Field(%v) out of bounds, struct type has %v fields: %v", i, gtype.NumFields(), t) 49 } 50 va := gtype.Field(i) 51 var rf reflect.StructField 52 if t.rtype != rTypeOfForward { 53 rf = t.rtype.Field(i) 54 } else { 55 // cannot dig in a forward-declared type, 56 // so try to resolve it 57 it := t.universe.gmap.At(t.gtype) 58 if it != nil { 59 rtype := it.(Type).ReflectType() 60 if rtype.Kind() != t.kind { 61 debugf("mismatched Forward type: <%v> has reflect.Type <%v>", t, rtype) 62 } 63 rf = rtype.Field(i) 64 } else { 65 // populate Field.Index and approximate Field.Type 66 rf.Index = []int{i} 67 rf.Type = rTypeOfForward 68 } 69 } 70 71 return StructField{ 72 Name: va.Name(), 73 Pkg: (*Package)(va.Pkg()), 74 Type: t.universe.maketype(va.Type(), rf.Type), // lock already held 75 Tag: rf.Tag, 76 Offset: rf.Offset, 77 Index: rf.Index, 78 Anonymous: va.Anonymous(), 79 } 80} 81 82// NumField returns a struct type's field count. 83// It panics if the type's Kind is not Struct. 84func (t *xtype) NumField() int { 85 if t.kind != reflect.Struct { 86 xerrorf(t, "NumField of non-struct type %v", t) 87 } 88 gtype := t.gunderlying().(*types.Struct) 89 return gtype.NumFields() 90} 91 92func (field *StructField) toReflectField(forceExported bool) reflect.StructField { 93 var pkgpath string 94 if pkg := field.Pkg; pkg != nil && !forceExported { 95 pkgpath = pkg.Path() 96 } 97 name := field.Name 98 if forceExported { 99 name = toExportedFieldName(name, field.Type, field.Anonymous) 100 } 101 return reflect.StructField{ 102 Name: name, 103 PkgPath: pkgpath, 104 Type: field.Type.ReflectType(), 105 Tag: field.Tag, 106 Offset: field.Offset, 107 Index: field.Index, 108 // reflect.StructOf() has very limited support for anonymous fields, 109 // do not even try to use it. 110 Anonymous: false, 111 } 112} 113 114func toReflectFields(fields []StructField, forceExported bool) []reflect.StructField { 115 rfields := make([]reflect.StructField, len(fields)) 116 for i := range fields { 117 rfields[i] = fields[i].toReflectField(forceExported) 118 } 119 return rfields 120} 121 122func (field *StructField) sanitize(i int) { 123 if len(field.Name) != 0 { 124 return 125 } 126 t := field.Type 127 name := t.Name() 128 if len(name) == 0 && t.Kind() == reflect.Ptr { 129 name = t.elem().Name() 130 } 131 if len(name) == 0 { 132 name = fmt.Sprintf("%s%d", StrGensymAnonymous, i) 133 } 134 field.Name = name 135 field.Anonymous = true 136} 137 138func (field *StructField) toGoField(i int) *types.Var { 139 field.sanitize(i) 140 return types.NewField(token.NoPos, (*types.Package)(field.Pkg), field.Name, field.Type.GoType(), field.Anonymous) 141} 142 143func toGoFields(fields []StructField) []*types.Var { 144 vars := make([]*types.Var, len(fields)) 145 for i := range fields { 146 vars[i] = fields[i].toGoField(i) 147 } 148 return vars 149} 150 151func (field *StructField) toTag() string { 152 return string(field.Tag) 153} 154 155func toTags(fields []StructField) []string { 156 tags := make([]string, len(fields)) 157 for i := range fields { 158 tags[i] = fields[i].toTag() 159 } 160 return tags 161} 162 163func toExportedFieldName(name string, t Type, anonymous bool) string { 164 if len(name) == 0 && unwrap(t) != nil { 165 if name = t.Name(); len(name) == 0 && t.Kind() == reflect.Ptr { 166 name = t.elem().Name() 167 } 168 } 169 if !ast.IsExported(name) { 170 if anonymous { 171 return GensymAnonymous(name) 172 } else { 173 return GensymPrivate(name) 174 } 175 } 176 return name 177} 178 179func (v *Universe) StructOf(fields []StructField) Type { 180 vars := toGoFields(fields) 181 tags := toTags(fields) 182 rfields := toReflectFields(fields, true) 183 return v.MakeType( 184 types.NewStruct(vars, tags), 185 reflect.StructOf(rfields), 186 ) 187} 188