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 * universe.go 12 * 13 * Created on May 14, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package xreflect 18 19import ( 20 "go/types" 21 "reflect" 22 // "runtime/debug" 23 "sync" 24 25 "github.com/cosmos72/gomacro/typeutil" 26) 27 28type Types struct { 29 gmap typeutil.Map 30} 31 32type Universe struct { 33 Types 34 // FromReflectType() map of types under construction. 35 // v.addmethods() will be invoked on them once the topmost FromReflectType() finishes. 36 partialTypes Types 37 ReflectTypes map[reflect.Type]Type 38 BasicTypes []Type 39 TypeOfInterface Type 40 TypeOfForward Type 41 TypeOfError Type 42 TryResolve func(name, pkgpath string) Type 43 Packages map[string]*Package 44 Importer types.ImporterFrom 45 RebuildDepth int 46 DebugDepth int 47 mutex sync.Mutex 48 debugmutex int 49 ThreadSafe bool 50 methodcache bool 51 fieldcache bool 52} 53 54func lock(v *Universe) *Universe { 55 if v.debugmutex != 0 { 56 errorf(nil, "deadlocking universe %p", v) 57 } 58 v.mutex.Lock() 59 v.debugmutex++ 60 return v 61} 62 63func un(v *Universe) { 64 // debugf("unlocking universe %p", v) 65 v.mutex.Unlock() 66 v.debugmutex-- 67} 68 69func (v *Universe) rebuild() bool { 70 return v.RebuildDepth > 0 71} 72 73func (v *Universe) cache(rt reflect.Type, t Type) Type { 74 if v.ReflectTypes == nil { 75 v.ReflectTypes = make(map[reflect.Type]Type) 76 } 77 v.ReflectTypes[rt] = t 78 // debugf("added rtype to cache: %v -> %v (%v)", rt, t, t.ReflectType()) 79 return t 80} 81 82// cachePackage0 recursively adds pkg and its imports to Universe.Packages if not cached already 83func (v *Universe) cachePackage0(pkg *types.Package) { 84 path := pkg.Path() 85 if _, ok := v.Packages[path]; ok { 86 return 87 } 88 v.Packages[path] = (*Package)(pkg) 89 for _, imp := range pkg.Imports() { 90 v.cachePackage0(imp) 91 } 92} 93 94// cachePackage unconditionally adds pkg to Universe.Packages, 95// then also adds its imports if not cached already 96func (v *Universe) cachePackage(pkg *types.Package) { 97 if pkg == nil { 98 return 99 } 100 if v.Packages == nil { 101 v.Packages = make(map[string]*Package) 102 } 103 v.Packages[pkg.Path()] = (*Package)(pkg) 104 for _, imp := range pkg.Imports() { 105 v.cachePackage0(imp) 106 } 107} 108 109// CachePackage unconditionally adds pkg to Universe.Packages, 110// then also adds its imports if not cached already 111func (v *Universe) CachePackage(pkg *types.Package) { 112 if pkg == nil { 113 return 114 } 115 if v.ThreadSafe { 116 defer un(lock(v)) 117 } 118 v.cachePackage(pkg) 119} 120 121// cacheMissingPackage adds a nil entry to Universe.Packages, if an entry is not present already. 122// Used to cache failures of Importer.Import. 123func (v *Universe) cacheMissingPackage(path string) { 124 if _, cached := v.Packages[path]; cached || len(path) == 0 { 125 return 126 } 127 if v.Packages == nil { 128 v.Packages = make(map[string]*Package) 129 } 130 v.Packages[path] = nil 131} 132 133func (v *Universe) importPackage(path string) *Package { 134 cachepkg, cached := v.Packages[path] 135 if cachepkg != nil { 136 return cachepkg 137 } 138 if v.Importer == nil { 139 v.Importer = DefaultImporter() 140 } 141 pkg, err := v.Importer.Import(path) 142 if err != nil || pkg == nil { 143 if !cached { 144 if v.debug() { 145 debugf("importer: cannot find package %q metadata, approximating it with reflection", path) 146 } 147 v.cacheMissingPackage(path) 148 } 149 return nil 150 } 151 // debugf("imported package %q", path) 152 v.cachePackage(pkg) 153 return (*Package)(pkg) 154} 155 156func (v *Universe) namedTypeFromImport(rtype reflect.Type) Type { 157 t := v.namedTypeFromPackageCache(rtype) 158 if unwrap(t) != nil { 159 return t 160 } 161 pkg := v.loadPackage(rtype.PkgPath()) 162 if pkg == nil { 163 return nil 164 } 165 return v.namedTypeFromPackage(rtype, (*types.Package)(pkg)) 166} 167 168func (v *Universe) namedTypeFromPackageCache(rtype reflect.Type) Type { 169 pkgpath := rtype.PkgPath() 170 pkg := (*types.Package)(v.Packages[pkgpath]) 171 if pkg != nil { 172 return v.namedTypeFromPackage(rtype, pkg) 173 } 174 return nil 175} 176 177func (v *Universe) namedTypeFromPackage(rtype reflect.Type, pkg *types.Package) Type { 178 name := rtype.Name() 179 if scope := pkg.Scope(); scope != nil && len(name) != 0 { 180 if obj := scope.Lookup(name); obj != nil { 181 if gtype := obj.Type(); gtype != nil { 182 // debugf("imported named type %v for %v", gtype, rtype) 183 // not v.MakeType, because we already hold the lock 184 return v.maketype3(gtypeToKind(nil, gtype), gtype, rtype) 185 } 186 } 187 } 188 return nil 189} 190