1package reflect2
2
3import (
4	"reflect"
5	"runtime"
6	"strings"
7	"sync"
8	"unsafe"
9)
10
11// typelinks1 for 1.5 ~ 1.6
12//go:linkname typelinks1 reflect.typelinks
13func typelinks1() [][]unsafe.Pointer
14
15// typelinks2 for 1.7 ~
16//go:linkname typelinks2 reflect.typelinks
17func typelinks2() (sections []unsafe.Pointer, offset [][]int32)
18
19// initOnce guards initialization of types and packages
20var initOnce sync.Once
21
22var types map[string]reflect.Type
23var packages map[string]map[string]reflect.Type
24
25// discoverTypes initializes types and packages
26func discoverTypes() {
27	types = make(map[string]reflect.Type)
28	packages = make(map[string]map[string]reflect.Type)
29
30	ver := runtime.Version()
31	if ver == "go1.5" || strings.HasPrefix(ver, "go1.5.") {
32		loadGo15Types()
33	} else if ver == "go1.6" || strings.HasPrefix(ver, "go1.6.") {
34		loadGo15Types()
35	} else {
36		loadGo17Types()
37	}
38}
39
40func loadGo15Types() {
41	var obj interface{} = reflect.TypeOf(0)
42	typePtrss := typelinks1()
43	for _, typePtrs := range typePtrss {
44		for _, typePtr := range typePtrs {
45			(*emptyInterface)(unsafe.Pointer(&obj)).word = typePtr
46			typ := obj.(reflect.Type)
47			if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct {
48				loadedType := typ.Elem()
49				pkgTypes := packages[loadedType.PkgPath()]
50				if pkgTypes == nil {
51					pkgTypes = map[string]reflect.Type{}
52					packages[loadedType.PkgPath()] = pkgTypes
53				}
54				types[loadedType.String()] = loadedType
55				pkgTypes[loadedType.Name()] = loadedType
56			}
57			if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Ptr &&
58				typ.Elem().Elem().Kind() == reflect.Struct {
59				loadedType := typ.Elem().Elem()
60				pkgTypes := packages[loadedType.PkgPath()]
61				if pkgTypes == nil {
62					pkgTypes = map[string]reflect.Type{}
63					packages[loadedType.PkgPath()] = pkgTypes
64				}
65				types[loadedType.String()] = loadedType
66				pkgTypes[loadedType.Name()] = loadedType
67			}
68		}
69	}
70}
71
72func loadGo17Types() {
73	var obj interface{} = reflect.TypeOf(0)
74	sections, offset := typelinks2()
75	for i, offs := range offset {
76		rodata := sections[i]
77		for _, off := range offs {
78			(*emptyInterface)(unsafe.Pointer(&obj)).word = resolveTypeOff(unsafe.Pointer(rodata), off)
79			typ := obj.(reflect.Type)
80			if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct {
81				loadedType := typ.Elem()
82				pkgTypes := packages[loadedType.PkgPath()]
83				if pkgTypes == nil {
84					pkgTypes = map[string]reflect.Type{}
85					packages[loadedType.PkgPath()] = pkgTypes
86				}
87				types[loadedType.String()] = loadedType
88				pkgTypes[loadedType.Name()] = loadedType
89			}
90		}
91	}
92}
93
94type emptyInterface struct {
95	typ  unsafe.Pointer
96	word unsafe.Pointer
97}
98
99// TypeByName return the type by its name, just like Class.forName in java
100func TypeByName(typeName string) Type {
101	initOnce.Do(discoverTypes)
102	return Type2(types[typeName])
103}
104
105// TypeByPackageName return the type by its package and name
106func TypeByPackageName(pkgPath string, name string) Type {
107	initOnce.Do(discoverTypes)
108	pkgTypes := packages[pkgPath]
109	if pkgTypes == nil {
110		return nil
111	}
112	return Type2(pkgTypes[name])
113}
114