1package reflect2
2
3import (
4	"github.com/modern-go/concurrent"
5	"reflect"
6	"unsafe"
7)
8
9type Type interface {
10	Kind() reflect.Kind
11	// New return pointer to data of this type
12	New() interface{}
13	// UnsafeNew return the allocated space pointed by unsafe.Pointer
14	UnsafeNew() unsafe.Pointer
15	// PackEFace cast a unsafe pointer to object represented pointer
16	PackEFace(ptr unsafe.Pointer) interface{}
17	// Indirect dereference object represented pointer to this type
18	Indirect(obj interface{}) interface{}
19	// UnsafeIndirect dereference pointer to this type
20	UnsafeIndirect(ptr unsafe.Pointer) interface{}
21	// Type1 returns reflect.Type
22	Type1() reflect.Type
23	Implements(thatType Type) bool
24	String() string
25	RType() uintptr
26	// interface{} of this type has pointer like behavior
27	LikePtr() bool
28	IsNullable() bool
29	IsNil(obj interface{}) bool
30	UnsafeIsNil(ptr unsafe.Pointer) bool
31	Set(obj interface{}, val interface{})
32	UnsafeSet(ptr unsafe.Pointer, val unsafe.Pointer)
33	AssignableTo(anotherType Type) bool
34}
35
36type ListType interface {
37	Type
38	Elem() Type
39	SetIndex(obj interface{}, index int, elem interface{})
40	UnsafeSetIndex(obj unsafe.Pointer, index int, elem unsafe.Pointer)
41	GetIndex(obj interface{}, index int) interface{}
42	UnsafeGetIndex(obj unsafe.Pointer, index int) unsafe.Pointer
43}
44
45type ArrayType interface {
46	ListType
47	Len() int
48}
49
50type SliceType interface {
51	ListType
52	MakeSlice(length int, cap int) interface{}
53	UnsafeMakeSlice(length int, cap int) unsafe.Pointer
54	Grow(obj interface{}, newLength int)
55	UnsafeGrow(ptr unsafe.Pointer, newLength int)
56	Append(obj interface{}, elem interface{})
57	UnsafeAppend(obj unsafe.Pointer, elem unsafe.Pointer)
58	LengthOf(obj interface{}) int
59	UnsafeLengthOf(ptr unsafe.Pointer) int
60	SetNil(obj interface{})
61	UnsafeSetNil(ptr unsafe.Pointer)
62	Cap(obj interface{}) int
63	UnsafeCap(ptr unsafe.Pointer) int
64}
65
66type StructType interface {
67	Type
68	NumField() int
69	Field(i int) StructField
70	FieldByName(name string) StructField
71	FieldByIndex(index []int) StructField
72	FieldByNameFunc(match func(string) bool) StructField
73}
74
75type StructField interface {
76	Offset() uintptr
77	Name() string
78	PkgPath() string
79	Type() Type
80	Tag() reflect.StructTag
81	Index() []int
82	Anonymous() bool
83	Set(obj interface{}, value interface{})
84	UnsafeSet(obj unsafe.Pointer, value unsafe.Pointer)
85	Get(obj interface{}) interface{}
86	UnsafeGet(obj unsafe.Pointer) unsafe.Pointer
87}
88
89type MapType interface {
90	Type
91	Key() Type
92	Elem() Type
93	MakeMap(cap int) interface{}
94	UnsafeMakeMap(cap int) unsafe.Pointer
95	SetIndex(obj interface{}, key interface{}, elem interface{})
96	UnsafeSetIndex(obj unsafe.Pointer, key unsafe.Pointer, elem unsafe.Pointer)
97	TryGetIndex(obj interface{}, key interface{}) (interface{}, bool)
98	GetIndex(obj interface{}, key interface{}) interface{}
99	UnsafeGetIndex(obj unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer
100	Iterate(obj interface{}) MapIterator
101	UnsafeIterate(obj unsafe.Pointer) MapIterator
102}
103
104type MapIterator interface {
105	HasNext() bool
106	Next() (key interface{}, elem interface{})
107	UnsafeNext() (key unsafe.Pointer, elem unsafe.Pointer)
108}
109
110type PtrType interface {
111	Type
112	Elem() Type
113}
114
115type InterfaceType interface {
116	NumMethod() int
117}
118
119type Config struct {
120	UseSafeImplementation bool
121}
122
123type API interface {
124	TypeOf(obj interface{}) Type
125	Type2(type1 reflect.Type) Type
126}
127
128var ConfigUnsafe = Config{UseSafeImplementation: false}.Froze()
129var ConfigSafe = Config{UseSafeImplementation: true}.Froze()
130
131type frozenConfig struct {
132	useSafeImplementation bool
133	cache                 *concurrent.Map
134}
135
136func (cfg Config) Froze() *frozenConfig {
137	return &frozenConfig{
138		useSafeImplementation: cfg.UseSafeImplementation,
139		cache: concurrent.NewMap(),
140	}
141}
142
143func (cfg *frozenConfig) TypeOf(obj interface{}) Type {
144	cacheKey := uintptr(unpackEFace(obj).rtype)
145	typeObj, found := cfg.cache.Load(cacheKey)
146	if found {
147		return typeObj.(Type)
148	}
149	return cfg.Type2(reflect.TypeOf(obj))
150}
151
152func (cfg *frozenConfig) Type2(type1 reflect.Type) Type {
153	if type1 == nil {
154		return nil
155	}
156	cacheKey := uintptr(unpackEFace(type1).data)
157	typeObj, found := cfg.cache.Load(cacheKey)
158	if found {
159		return typeObj.(Type)
160	}
161	type2 := cfg.wrapType(type1)
162	cfg.cache.Store(cacheKey, type2)
163	return type2
164}
165
166func (cfg *frozenConfig) wrapType(type1 reflect.Type) Type {
167	safeType := safeType{Type: type1, cfg: cfg}
168	switch type1.Kind() {
169	case reflect.Struct:
170		if cfg.useSafeImplementation {
171			return &safeStructType{safeType}
172		}
173		return newUnsafeStructType(cfg, type1)
174	case reflect.Array:
175		if cfg.useSafeImplementation {
176			return &safeSliceType{safeType}
177		}
178		return newUnsafeArrayType(cfg, type1)
179	case reflect.Slice:
180		if cfg.useSafeImplementation {
181			return &safeSliceType{safeType}
182		}
183		return newUnsafeSliceType(cfg, type1)
184	case reflect.Map:
185		if cfg.useSafeImplementation {
186			return &safeMapType{safeType}
187		}
188		return newUnsafeMapType(cfg, type1)
189	case reflect.Ptr, reflect.Chan, reflect.Func:
190		if cfg.useSafeImplementation {
191			return &safeMapType{safeType}
192		}
193		return newUnsafePtrType(cfg, type1)
194	case reflect.Interface:
195		if cfg.useSafeImplementation {
196			return &safeMapType{safeType}
197		}
198		if type1.NumMethod() == 0 {
199			return newUnsafeEFaceType(cfg, type1)
200		}
201		return newUnsafeIFaceType(cfg, type1)
202	default:
203		if cfg.useSafeImplementation {
204			return &safeType
205		}
206		return newUnsafeType(cfg, type1)
207	}
208}
209
210func TypeOf(obj interface{}) Type {
211	return ConfigUnsafe.TypeOf(obj)
212}
213
214func TypeOfPtr(obj interface{}) PtrType {
215	return TypeOf(obj).(PtrType)
216}
217
218func Type2(type1 reflect.Type) Type {
219	if type1 == nil {
220		return nil
221	}
222	return ConfigUnsafe.Type2(type1)
223}
224
225func PtrTo(typ Type) Type {
226	return Type2(reflect.PtrTo(typ.Type1()))
227}
228
229func PtrOf(obj interface{}) unsafe.Pointer {
230	return unpackEFace(obj).data
231}
232
233func RTypeOf(obj interface{}) uintptr {
234	return uintptr(unpackEFace(obj).rtype)
235}
236
237func IsNil(obj interface{}) bool {
238	if obj == nil {
239		return true
240	}
241	return unpackEFace(obj).data == nil
242}
243
244func IsNullable(kind reflect.Kind) bool {
245	switch kind {
246	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func, reflect.Slice, reflect.Interface:
247		return true
248	}
249	return false
250}
251
252func likePtrKind(kind reflect.Kind) bool {
253	switch kind {
254	case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func:
255		return true
256	}
257	return false
258}
259
260func likePtrType(typ reflect.Type) bool {
261	if likePtrKind(typ.Kind()) {
262		return true
263	}
264	if typ.Kind() == reflect.Struct {
265		if typ.NumField() != 1 {
266			return false
267		}
268		return likePtrType(typ.Field(0).Type)
269	}
270	if typ.Kind() == reflect.Array {
271		if typ.Len() != 1 {
272			return false
273		}
274		return likePtrType(typ.Elem())
275	}
276	return false
277}
278
279// NoEscape hides a pointer from escape analysis.  noescape is
280// the identity function but escape analysis doesn't think the
281// output depends on the input.  noescape is inlined and currently
282// compiles down to zero instructions.
283// USE CAREFULLY!
284//go:nosplit
285func NoEscape(p unsafe.Pointer) unsafe.Pointer {
286	x := uintptr(p)
287	return unsafe.Pointer(x ^ 0)
288}
289
290func UnsafeCastString(str string) []byte {
291	stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
292	sliceHeader := &reflect.SliceHeader{
293		Data: stringHeader.Data,
294		Cap: stringHeader.Len,
295		Len: stringHeader.Len,
296	}
297	return *(*[]byte)(unsafe.Pointer(sliceHeader))
298}
299